diff --git a/.mailmap b/.mailmap index 601064b77d43..c072f6282f19 100644 --- a/.mailmap +++ b/.mailmap @@ -458,6 +458,7 @@ Philipp Matthias Schäfer phosphorus Pierre Krieger pierwill <19642016+pierwill@users.noreply.github.com> +Pietro Albini Pradyumna Rahul Przemysław Wesołek Przemek Wesołek r00ster @@ -495,6 +496,8 @@ Ryan Wiedemann S Pradeep Kumar Sam Radhakrishnan Samuel Tardieu +Santiago Pastorino +Santiago Pastorino Scott McMurray Scott Olson Scott Olson Sean Gillespie swgillespie diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0aeff22ca9f8..89775bdee263 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -457,7 +457,7 @@ pub fn lower_to_hir(tcx: TyCtxt<'_>, (): ()) -> hir::Crate<'_> { // Don't hash unless necessary, because it's expensive. let opt_hir_hash = - if tcx.sess.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None }; + if tcx.needs_crate_hash() { Some(compute_hir_hash(tcx, &owners)) } else { None }; hir::Crate { owners, opt_hir_hash } } @@ -648,7 +648,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let bodies = SortedMap::from_presorted_elements(bodies); // Don't hash unless necessary, because it's expensive. - let (opt_hash_including_bodies, attrs_hash) = if self.tcx.sess.needs_crate_hash() { + let (opt_hash_including_bodies, attrs_hash) = if self.tcx.needs_crate_hash() { self.tcx.with_stable_hashing_context(|mut hcx| { let mut stable_hasher = StableHasher::new(); hcx.with_hir_bodies(node.def_id(), &bodies, |hcx| { diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl index 2f0ac0c2b198..f323bb4c254e 100644 --- a/compiler/rustc_ast_passes/messages.ftl +++ b/compiler/rustc_ast_passes/messages.ftl @@ -239,5 +239,10 @@ ast_passes_visibility_not_permitted = .individual_impl_items = place qualifiers on individual impl items instead .individual_foreign_items = place qualifiers on individual foreign items instead -ast_passes_where_after_type_alias = where clauses are not allowed after the type for type aliases +ast_passes_where_clause_after_type_alias = where clauses are not allowed after the type for type aliases + .note = see issue #112792 for more information + .help = add `#![feature(lazy_type_alias)]` to the crate attributes to enable + +ast_passes_where_clause_before_type_alias = where clauses are not allowed before the type for type aliases .note = see issue #89122 for more information + .suggestion = move it to the end of the type declaration diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index af594a00705f..79fc9e4382fa 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -136,40 +136,42 @@ impl<'a> AstValidator<'a> { } } - fn check_gat_where( + fn check_type_alias_where_clause_location( &mut self, - id: NodeId, - before_predicates: &[WherePredicate], - where_clauses: (ast::TyAliasWhereClause, ast::TyAliasWhereClause), - ) { - if !before_predicates.is_empty() { - let mut state = State::new(); - if !where_clauses.1.0 { - state.space(); - state.word_space("where"); - } else { + ty_alias: &TyAlias, + ) -> Result<(), errors::WhereClauseBeforeTypeAlias> { + let before_predicates = + ty_alias.generics.where_clause.predicates.split_at(ty_alias.where_predicates_split).0; + + if ty_alias.ty.is_none() || before_predicates.is_empty() { + return Ok(()); + } + + let mut state = State::new(); + if !ty_alias.where_clauses.1.0 { + state.space(); + state.word_space("where"); + } else { + state.word_space(","); + } + let mut first = true; + for p in before_predicates { + if !first { state.word_space(","); } - let mut first = true; - for p in before_predicates.iter() { - if !first { - state.word_space(","); - } - first = false; - state.print_where_predicate(p); - } - let suggestion = state.s.eof(); - self.lint_buffer.buffer_lint_with_diagnostic( - DEPRECATED_WHERE_CLAUSE_LOCATION, - id, - where_clauses.0.1, - fluent::ast_passes_deprecated_where_clause_location, - BuiltinLintDiagnostics::DeprecatedWhereclauseLocation( - where_clauses.1.1.shrink_to_hi(), - suggestion, - ), - ); + first = false; + state.print_where_predicate(p); } + + let span = ty_alias.where_clauses.0.1; + Err(errors::WhereClauseBeforeTypeAlias { + span, + sugg: errors::WhereClauseBeforeTypeAliasSugg { + left: span, + snippet: state.s.eof(), + right: ty_alias.where_clauses.1.1.shrink_to_hi(), + }, + }) } fn with_impl_trait(&mut self, outer: Option, f: impl FnOnce(&mut Self)) { @@ -1009,7 +1011,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> { replace_span: self.ending_semi_or_hi(item.span), }); } - ItemKind::TyAlias(box TyAlias { defaultness, where_clauses, bounds, ty, .. }) => { + ItemKind::TyAlias( + ty_alias @ box TyAlias { defaultness, bounds, where_clauses, ty, .. }, + ) => { self.check_defaultness(item.span, *defaultness); if ty.is_none() { self.session.emit_err(errors::TyAliasWithoutBody { @@ -1018,9 +1022,16 @@ impl<'a> Visitor<'a> for AstValidator<'a> { }); } self.check_type_no_bounds(bounds, "this context"); - if where_clauses.1.0 { - self.err_handler() - .emit_err(errors::WhereAfterTypeAlias { span: where_clauses.1.1 }); + + if self.session.features_untracked().lazy_type_alias { + if let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { + self.err_handler().emit_err(err); + } + } else if where_clauses.1.0 { + self.err_handler().emit_err(errors::WhereClauseAfterTypeAlias { + span: where_clauses.1.1, + help: self.session.is_nightly_build().then_some(()), + }); } } _ => {} @@ -1313,18 +1324,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } - if let AssocItemKind::Type(box TyAlias { - generics, - where_clauses, - where_predicates_split, - ty: Some(_), - .. - }) = &item.kind + if let AssocItemKind::Type(ty_alias) = &item.kind + && let Err(err) = self.check_type_alias_where_clause_location(ty_alias) { - self.check_gat_where( + self.lint_buffer.buffer_lint_with_diagnostic( + DEPRECATED_WHERE_CLAUSE_LOCATION, item.id, - generics.where_clause.predicates.split_at(*where_predicates_split).0, - *where_clauses, + err.span, + fluent::ast_passes_deprecated_where_clause_location, + BuiltinLintDiagnostics::DeprecatedWhereclauseLocation( + err.sugg.right, + err.sugg.snippet, + ), ); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index ab8015c4a43b..a6f217d4780f 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -496,11 +496,37 @@ pub struct FieldlessUnion { } #[derive(Diagnostic)] -#[diag(ast_passes_where_after_type_alias)] +#[diag(ast_passes_where_clause_after_type_alias)] #[note] -pub struct WhereAfterTypeAlias { +pub struct WhereClauseAfterTypeAlias { #[primary_span] pub span: Span, + #[help] + pub help: Option<()>, +} + +#[derive(Diagnostic)] +#[diag(ast_passes_where_clause_before_type_alias)] +#[note] +pub struct WhereClauseBeforeTypeAlias { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sugg: WhereClauseBeforeTypeAliasSugg, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + ast_passes_suggestion, + applicability = "machine-applicable", + style = "verbose" +)] +pub struct WhereClauseBeforeTypeAliasSugg { + #[suggestion_part(code = "")] + pub left: Span, + pub snippet: String, + #[suggestion_part(code = "{snippet}")] + pub right: Span, } #[derive(Diagnostic)] diff --git a/compiler/rustc_builtin_macros/src/format.rs b/compiler/rustc_builtin_macros/src/format.rs index 590db12a4cd0..ede95dbf897e 100644 --- a/compiler/rustc_builtin_macros/src/format.rs +++ b/compiler/rustc_builtin_macros/src/format.rs @@ -1,6 +1,6 @@ use rustc_ast::ptr::P; -use rustc_ast::token; use rustc_ast::tokenstream::TokenStream; +use rustc_ast::{token, StmtKind}; use rustc_ast::{ Expr, ExprKind, FormatAlignment, FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind, FormatArguments, FormatCount, @@ -163,7 +163,7 @@ fn make_format_args( let MacroInput { fmtstr: efmt, mut args, is_direct_literal } = input; - let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt, msg) { + let (fmt_str, fmt_style, fmt_span) = match expr_to_spanned_string(ecx, efmt.clone(), msg) { Ok(mut fmt) if append_newline => { fmt.0 = Symbol::intern(&format!("{}\n", fmt.0)); fmt @@ -171,17 +171,33 @@ fn make_format_args( Ok(fmt) => fmt, Err(err) => { if let Some((mut err, suggested)) = err { - let sugg_fmt = match args.explicit_args().len() { - 0 => "{}".to_string(), - _ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())), - }; if !suggested { - err.span_suggestion( - unexpanded_fmt_span.shrink_to_lo(), - "you might be missing a string literal to format with", - format!("\"{sugg_fmt}\", "), - Applicability::MaybeIncorrect, - ); + if let ExprKind::Block(block, None) = &efmt.kind + && block.stmts.len() == 1 + && let StmtKind::Expr(expr) = &block.stmts[0].kind + && let ExprKind::Path(None, path) = &expr.kind + && path.is_potential_trivial_const_arg() + { + err.multipart_suggestion( + "quote your inlined format argument to use as string literal", + vec![ + (unexpanded_fmt_span.shrink_to_hi(), "\"".to_string()), + (unexpanded_fmt_span.shrink_to_lo(), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + let sugg_fmt = match args.explicit_args().len() { + 0 => "{}".to_string(), + _ => format!("{}{{}}", "{} ".repeat(args.explicit_args().len())), + }; + err.span_suggestion( + unexpanded_fmt_span.shrink_to_lo(), + "you might be missing a string literal to format with", + format!("\"{sugg_fmt}\", "), + Applicability::MaybeIncorrect, + ); + } } err.emit(); } diff --git a/compiler/rustc_codegen_cranelift/src/driver/jit.rs b/compiler/rustc_codegen_cranelift/src/driver/jit.rs index 3ea388421480..1c606494f383 100644 --- a/compiler/rustc_codegen_cranelift/src/driver/jit.rs +++ b/compiler/rustc_codegen_cranelift/src/driver/jit.rs @@ -98,7 +98,7 @@ pub(crate) fn run_jit(tcx: TyCtxt<'_>, backend_config: BackendConfig) -> ! { tcx.sess.fatal("JIT mode doesn't work with `cargo check`"); } - if !tcx.sess.crate_types().contains(&rustc_session::config::CrateType::Executable) { + if !tcx.crate_types().contains(&rustc_session::config::CrateType::Executable) { tcx.sess.fatal("can't jit non-executable crate"); } diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index b4f7e20e05da..24fd5bbf8c52 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -209,7 +209,7 @@ pub unsafe fn create_module<'ll>( // PIE is potentially more effective than PIC, but can only be used in executables. // If all our outputs are executables, then we can relax PIC to PIE. if reloc_model == RelocModel::Pie - || sess.crate_types().iter().all(|ty| *ty == CrateType::Executable) + || tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable) { llvm::LLVMRustSetModulePIELevel(llmod); } diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs index db9ba0abbde8..97a99e510567 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs @@ -12,8 +12,7 @@ use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::mir::coverage::CodeRegion; use rustc_middle::ty::TyCtxt; - -use std::ffi::CString; +use rustc_span::Symbol; /// Generates and exports the Coverage Map. /// @@ -89,7 +88,10 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { // Encode all filenames referenced by counters/expressions in this module let filenames_buffer = llvm::build_byte_buffer(|filenames_buffer| { - coverageinfo::write_filenames_section_to_buffer(&mapgen.filenames, filenames_buffer); + coverageinfo::write_filenames_section_to_buffer( + mapgen.filenames.iter().map(Symbol::as_str), + filenames_buffer, + ); }); let filenames_size = filenames_buffer.len(); @@ -117,7 +119,7 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) { } struct CoverageMapGenerator { - filenames: FxIndexSet, + filenames: FxIndexSet, } impl CoverageMapGenerator { @@ -128,11 +130,10 @@ impl CoverageMapGenerator { // Since rustc generates coverage maps with relative paths, the // compilation directory can be combined with the relative paths // to get absolute paths, if needed. - let working_dir = - tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy().to_string(); - let c_filename = - CString::new(working_dir).expect("null error converting filename to C string"); - filenames.insert(c_filename); + let working_dir = Symbol::intern( + &tcx.sess.opts.working_dir.remapped_path_if_available().to_string_lossy(), + ); + filenames.insert(working_dir); Self { filenames } } @@ -170,10 +171,8 @@ impl CoverageMapGenerator { current_file_id += 1; } current_file_name = Some(file_name); - let c_filename = CString::new(file_name.to_string()) - .expect("null error converting filename to C string"); - debug!(" file_id: {} = '{:?}'", current_file_id, c_filename); - let (filenames_index, _) = self.filenames.insert_full(c_filename); + debug!(" file_id: {} = '{:?}'", current_file_id, file_name); + let (filenames_index, _) = self.filenames.insert_full(file_name); virtual_file_mapping.push(filenames_index as u32); } debug!("Adding counter {:?} to map for {:?}", counter, region); diff --git a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs index afceb7531e68..621fd36b2a32 100644 --- a/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs +++ b/compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs @@ -25,7 +25,6 @@ use rustc_middle::ty::Instance; use rustc_middle::ty::Ty; use std::cell::RefCell; -use std::ffi::CString; pub(crate) mod ffi; pub(crate) mod map_data; @@ -332,21 +331,32 @@ fn create_pgo_func_name_var<'ll, 'tcx>( cx: &CodegenCx<'ll, 'tcx>, instance: Instance<'tcx>, ) -> &'ll llvm::Value { - let mangled_fn_name = CString::new(cx.tcx.symbol_name(instance).name) - .expect("error converting function name to C string"); + let mangled_fn_name: &str = cx.tcx.symbol_name(instance).name; let llfn = cx.get_fn(instance); - unsafe { llvm::LLVMRustCoverageCreatePGOFuncNameVar(llfn, mangled_fn_name.as_ptr()) } + unsafe { + llvm::LLVMRustCoverageCreatePGOFuncNameVar( + llfn, + mangled_fn_name.as_ptr().cast(), + mangled_fn_name.len(), + ) + } } pub(crate) fn write_filenames_section_to_buffer<'a>( - filenames: impl IntoIterator, + filenames: impl IntoIterator, buffer: &RustString, ) { - let c_str_vec = filenames.into_iter().map(|cstring| cstring.as_ptr()).collect::>(); + let (pointers, lengths) = filenames + .into_iter() + .map(|s: &str| (s.as_ptr().cast(), s.len())) + .unzip::<_, _, Vec<_>, Vec<_>>(); + unsafe { llvm::LLVMRustCoverageWriteFilenamesSectionToBuffer( - c_str_vec.as_ptr(), - c_str_vec.len(), + pointers.as_ptr(), + pointers.len(), + lengths.as_ptr(), + lengths.len(), buffer, ); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs index ef7c661936a0..425e935bc9f2 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/gdb.rs @@ -92,7 +92,7 @@ pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx<'_, '_>) -> bool { // each rlib could produce a different set of visualizers that would be embedded // in the `.debug_gdb_scripts` section. For that reason, we make sure that the // section is only emitted for leaf crates. - let embed_visualizers = cx.sess().crate_types().iter().any(|&crate_type| match crate_type { + let embed_visualizers = cx.tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib | CrateType::Staticlib => { // These are crate types for which we will embed pretty printers since they // are treated as leaf crates. diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 112b4a173a57..84157d1e25ca 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1707,6 +1707,8 @@ extern "C" { pub fn LLVMRustCoverageWriteFilenamesSectionToBuffer( Filenames: *const *const c_char, FilenamesLen: size_t, + Lengths: *const size_t, + LengthsLen: size_t, BufferOut: &RustString, ); @@ -1721,7 +1723,11 @@ extern "C" { BufferOut: &RustString, ); - pub fn LLVMRustCoverageCreatePGOFuncNameVar(F: &Value, FuncName: *const c_char) -> &Value; + pub fn LLVMRustCoverageCreatePGOFuncNameVar( + F: &Value, + FuncName: *const c_char, + FuncNameLen: size_t, + ) -> &Value; pub fn LLVMRustCoverageHashByteArray(Bytes: *const c_char, NumBytes: size_t) -> u64; #[allow(improper_ctypes)] diff --git a/compiler/rustc_codegen_llvm/src/mono_item.rs b/compiler/rustc_codegen_llvm/src/mono_item.rs index e8cda626f549..38e8220569a2 100644 --- a/compiler/rustc_codegen_llvm/src/mono_item.rs +++ b/compiler/rustc_codegen_llvm/src/mono_item.rs @@ -111,7 +111,7 @@ impl CodegenCx<'_, '_> { } // Symbols from executables can't really be imported any further. - let all_exe = self.tcx.sess.crate_types().iter().all(|ty| *ty == CrateType::Executable); + let all_exe = self.tcx.crate_types().iter().all(|ty| *ty == CrateType::Executable); let is_declaration_for_linker = is_declaration || linkage == llvm::Linkage::AvailableExternallyLinkage; if all_exe && !is_declaration_for_linker { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index cd6201648ee9..bb65c3c81d95 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -69,7 +69,7 @@ pub fn link_binary<'a>( let _timer = sess.timer("link_binary"); let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); - for &crate_type in sess.crate_types().iter() { + for &crate_type in &codegen_results.crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) && !output_metadata diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index a1e322f4b313..11afe0fbc3c9 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1703,7 +1703,7 @@ fn exported_symbols_for_proc_macro_crate(tcx: TyCtxt<'_>) -> Vec { return Vec::new(); } - let stable_crate_id = tcx.sess.local_stable_crate_id(); + let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); let proc_macro_decls_name = tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); let metadata_symbol_name = exported_symbols::metadata_symbol_name(tcx); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index 326b28ad1041..8fb2ccb7e8a4 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -19,7 +19,7 @@ use rustc_session::config::{CrateType, OomStrategy}; use rustc_target::spec::SanitizerSet; pub fn threshold(tcx: TyCtxt<'_>) -> SymbolExportLevel { - crates_export_threshold(&tcx.sess.crate_types()) + crates_export_threshold(tcx.crate_types()) } fn crate_export_threshold(crate_type: CrateType) -> SymbolExportLevel { @@ -290,8 +290,8 @@ fn exported_symbols_provider_local( })); } - if tcx.sess.crate_types().contains(&CrateType::Dylib) - || tcx.sess.crate_types().contains(&CrateType::ProcMacro) + if tcx.crate_types().contains(&CrateType::Dylib) + || tcx.crate_types().contains(&CrateType::ProcMacro) { let symbol_name = metadata_symbol_name(tcx); let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name)); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 407185257419..f485af00bcad 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -123,7 +123,7 @@ pub struct ModuleConfig { impl ModuleConfig { fn new( kind: ModuleKind, - sess: &Session, + tcx: TyCtxt<'_>, no_builtins: bool, is_compiler_builtins: bool, ) -> ModuleConfig { @@ -135,6 +135,7 @@ impl ModuleConfig { }; } + let sess = tcx.sess; let opt_level_and_size = if_regular!(Some(sess.opts.optimize), None); let save_temps = sess.opts.cg.save_temps; @@ -166,7 +167,7 @@ impl ModuleConfig { // `#![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(sess) { + } else if need_bitcode_in_object(tcx) { EmitObj::ObjectCode(BitcodeSection::Full) } else { EmitObj::ObjectCode(BitcodeSection::None) @@ -414,9 +415,10 @@ pub struct CompiledModules { pub allocator_module: Option, } -fn need_bitcode_in_object(sess: &Session) -> bool { +fn need_bitcode_in_object(tcx: TyCtxt<'_>) -> bool { + let sess = tcx.sess; let requested_for_rlib = sess.opts.cg.embed_bitcode - && sess.crate_types().contains(&CrateType::Rlib) + && tcx.crate_types().contains(&CrateType::Rlib) && sess.opts.output_types.contains_key(&OutputType::Exe); let forced_by_target = sess.target.forces_embed_bitcode; requested_for_rlib || forced_by_target @@ -450,11 +452,11 @@ pub fn start_async_codegen( let crate_info = CrateInfo::new(tcx, target_cpu); let regular_config = - ModuleConfig::new(ModuleKind::Regular, sess, no_builtins, is_compiler_builtins); + ModuleConfig::new(ModuleKind::Regular, tcx, no_builtins, is_compiler_builtins); let metadata_config = - ModuleConfig::new(ModuleKind::Metadata, sess, no_builtins, is_compiler_builtins); + ModuleConfig::new(ModuleKind::Metadata, tcx, no_builtins, is_compiler_builtins); let allocator_config = - ModuleConfig::new(ModuleKind::Allocator, sess, no_builtins, is_compiler_builtins); + ModuleConfig::new(ModuleKind::Allocator, tcx, no_builtins, is_compiler_builtins); let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); let (codegen_worker_send, codegen_worker_receive) = channel(); @@ -1092,7 +1094,7 @@ fn start_executing_work( }; let cgcx = CodegenContext:: { - crate_types: sess.crate_types().to_vec(), + crate_types: tcx.crate_types().to_vec(), each_linked_rlib_for_lto, lto: sess.lto(), fewer_names: sess.fewer_names(), @@ -2063,7 +2065,7 @@ fn msvc_imps_needed(tcx: TyCtxt<'_>) -> bool { ); tcx.sess.target.is_like_windows && - tcx.sess.crate_types().iter().any(|ct| *ct == CrateType::Rlib) && + tcx.crate_types().iter().any(|ct| *ct == CrateType::Rlib) && // ThinLTO can't handle this workaround in all cases, so we don't // emit the `__imp_` symbols. Instead we make them unnecessary by disallowing // dynamic linking when linker plugin LTO is enabled. diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4819ab22723d..aa003e4e8981 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -779,18 +779,13 @@ pub fn codegen_crate( impl CrateInfo { pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { - let exported_symbols = tcx - .sess - .crate_types() + let crate_types = tcx.crate_types().to_vec(); + let exported_symbols = crate_types .iter() .map(|&c| (c, crate::back::linker::exported_symbols(tcx, c))) .collect(); - let linked_symbols = tcx - .sess - .crate_types() - .iter() - .map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))) - .collect(); + let linked_symbols = + crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID); let subsystem = attr::first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem); @@ -829,6 +824,7 @@ impl CrateInfo { let mut info = CrateInfo { target_cpu, + crate_types, exported_symbols, linked_symbols, local_crate_name, @@ -916,7 +912,7 @@ impl CrateInfo { }); } - let embed_visualizers = tcx.sess.crate_types().iter().any(|&crate_type| match crate_type { + let embed_visualizers = tcx.crate_types().iter().any(|&crate_type| match crate_type { CrateType::Executable | CrateType::Dylib | CrateType::Cdylib => { // These are crate types for which we invoke the linker and can embed // NatVis visualizers. @@ -1013,7 +1009,7 @@ fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> CguR match compute_per_cgu_lto_type( &tcx.sess.lto(), &tcx.sess.opts, - &tcx.sess.crate_types(), + tcx.crate_types(), ModuleKind::Regular, ) { ComputedLtoType::No => CguReuse::PostLto, diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index be4c81638d63..f577f653ccd6 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -150,6 +150,7 @@ impl From<&cstore::NativeLib> for NativeLib { #[derive(Debug, Encodable, Decodable)] pub struct CrateInfo { pub target_cpu: String, + pub crate_types: Vec, pub exported_symbols: FxHashMap>, pub linked_symbols: FxHashMap>, pub local_crate_name: Symbol, diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index ec77569d4b87..7bbed0877f07 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -653,8 +653,6 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation { if sess.opts.unstable_opts.link_only { if let Input::File(file) = &sess.io.input { - // FIXME: #![crate_type] and #![crate_name] support not implemented yet - sess.init_crate_types(collect_crate_types(sess, &[])); let outputs = compiler.build_output_filenames(sess, &[]); let rlink_data = fs::read(file).unwrap_or_else(|err| { sess.emit_fatal(RlinkUnableToRead { err }); diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 92a91766d913..6340f1dcca1c 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -1843,7 +1843,7 @@ impl Expr<'_> { .iter() .map(|field| field.expr) .chain(init.into_iter()) - .all(|e| e.can_have_side_effects()), + .any(|e| e.can_have_side_effects()), ExprKind::Array(args) | ExprKind::Tup(args) @@ -1857,7 +1857,7 @@ impl Expr<'_> { .. }, args, - ) => args.iter().all(|arg| arg.can_have_side_effects()), + ) => args.iter().any(|arg| arg.can_have_side_effects()), ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index a8c66ff90011..044fb405e321 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1,7 +1,7 @@ use super::potentially_plural_count; use crate::errors::LifetimesOrBoundsMismatchOnTrait; use hir::def_id::{DefId, LocalDefId}; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_errors::{ pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed, MultiSpan, }; @@ -265,7 +265,6 @@ fn compare_method_predicate_entailment<'tcx>( infer::HigherRankedType, tcx.fn_sig(impl_m.def_id).instantiate_identity(), ); - let unnormalized_impl_fty = Ty::new_fn_ptr(tcx, ty::Binder::dummy(unnormalized_impl_sig)); let norm_cause = ObligationCause::misc(impl_m_span, impl_m_def_id); let impl_sig = ocx.normalize(&norm_cause, param_env, unnormalized_impl_sig); @@ -309,16 +308,44 @@ fn compare_method_predicate_entailment<'tcx>( } if check_implied_wf == CheckImpliedWfMode::Check && !(impl_sig, trait_sig).references_error() { - // We need to check that the impl's args are well-formed given - // the hybrid param-env (impl + trait method where-clauses). - ocx.register_obligation(traits::Obligation::new( - infcx.tcx, - ObligationCause::dummy(), - param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - unnormalized_impl_fty.into(), - ))), - )); + // See #108544. Annoying, we can end up in cases where, because of winnowing, + // we pick param env candidates over a more general impl, leading to more + // stricter lifetime requirements than we would otherwise need. This can + // trigger the lint. Instead, let's only consider type outlives and + // region outlives obligations. + // + // FIXME(-Ztrait-solver=next): Try removing this hack again once + // the new solver is stable. + let mut wf_args: smallvec::SmallVec<[_; 4]> = + unnormalized_impl_sig.inputs_and_output.iter().map(|ty| ty.into()).collect(); + // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) + // will give back the well-formed predicate of the same array. + let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); + while let Some(arg) = wf_args.pop() { + let Some(obligations) = rustc_trait_selection::traits::wf::obligations( + infcx, + param_env, + impl_m_def_id, + 0, + arg, + impl_m_span, + ) else { + continue; + }; + for obligation in obligations { + match obligation.predicate.kind().skip_binder() { + ty::PredicateKind::Clause( + ty::ClauseKind::RegionOutlives(..) | ty::ClauseKind::TypeOutlives(..), + ) => ocx.register_obligation(obligation), + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + if wf_args_seen.insert(arg) { + wf_args.push(arg) + } + } + _ => {} + } + } + } } // Check that all obligations are satisfied by the implementation's diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index ba00954db918..dd79d1afc62f 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -645,18 +645,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) } hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee_expr.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MaybeIncorrect, - ); - } if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { inner_callee_path = Some(inner_qpath); self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) @@ -668,6 +656,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }; if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { + // If the call spans more than one line and the callee kind is + // itself another `ExprCall`, that's a clue that we might just be + // missing a semicolon (#51055, #106515). + let call_is_multiline = self + .tcx + .sess + .source_map() + .is_multiline(call_expr.span.with_lo(callee_expr.span.hi())) + && call_expr.span.ctxt() == callee_expr.span.ctxt(); + if call_is_multiline { + err.span_suggestion( + callee_expr.span.shrink_to_hi(), + "consider using a semicolon here to finish the statement", + ";", + Applicability::MaybeIncorrect, + ); + } if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_ty) && !self.type_is_sized_modulo_regions(self.param_env, output_ty) { diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index d9b17a2e2b77..1cfdc5b9e7f5 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -510,9 +510,11 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { success(adjustments, ty, obligations) } - // &[T; n] or &mut [T; n] -> &[T] - // or &mut [T; n] -> &mut [T] - // or &Concrete -> &Trait, etc. + /// Performs [unsized coercion] by emulating a fulfillment loop on a + /// `CoerceUnsized` goal until all `CoerceUnsized` and `Unsize` goals + /// are successfully selected. + /// + /// [unsized coercion](https://doc.rust-lang.org/reference/type-coercions.html#unsized-coercions) #[instrument(skip(self), level = "debug")] fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> { source = self.shallow_resolve(source); @@ -1039,6 +1041,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Returns false if the coercion creates any obligations that result in /// errors. pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { + // FIXME(-Ztrait-solver=next): We need to structurally resolve both types here. let source = self.resolve_vars_with_obligations(expr_ty); debug!("coercion::can_with_predicates({:?} -> {:?})", source, target); diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs index 48383bd90fef..b05cef56ad21 100644 --- a/compiler/rustc_hir_typeck/src/demand.rs +++ b/compiler/rustc_hir_typeck/src/demand.rs @@ -260,7 +260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { )); let expr = expr.peel_drop_temps(); let cause = self.misc(expr.span); - let expr_ty = self.resolve_vars_with_obligations(checked_ty); + let expr_ty = self.resolve_vars_if_possible(checked_ty); let mut err = self.err_ctxt().report_mismatched_types(&cause, expected, expr_ty, e); let is_insufficiently_polymorphic = diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 8fad0a6c9e49..28fe2e062e58 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -85,16 +85,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// to get more type information. // FIXME(-Ztrait-solver=next): A lot of the calls to this method should // probably be `try_structurally_resolve_type` or `structurally_resolve_type` instead. - pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) - } - - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] - pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( - &self, - mut ty: Ty<'tcx>, - mutate_fulfillment_errors: impl Fn(&mut Vec>), - ) -> Ty<'tcx> { + #[instrument(skip(self), level = "debug", ret)] + pub(in super::super) fn resolve_vars_with_obligations(&self, mut ty: Ty<'tcx>) -> Ty<'tcx> { // No Infer()? Nothing needs doing. if !ty.has_non_region_infer() { debug!("no inference var, nothing needs doing"); @@ -112,7 +104,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // possible. This can help substantially when there are // indirect dependencies that don't seem worth tracking // precisely. - self.select_obligations_where_possible(mutate_fulfillment_errors); + self.select_obligations_where_possible(|_| {}); self.resolve_vars_if_possible(ty) } @@ -626,8 +618,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { match *self_ty.kind() { ty::Infer(ty::TyVar(found_vid)) => { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. let found_vid = self.root_var(found_vid); debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); expected_vid == found_vid @@ -642,8 +632,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self_ty: ty::TyVid, ) -> impl DoubleEndedIterator> + Captures<'tcx> + 'b { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. let ty_var_root = self.root_var(self_ty); trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 89bbb4c22037..48358e338dae 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -950,7 +950,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if !expected.is_unit() { return; } - let found = self.resolve_vars_with_obligations(found); + let found = self.resolve_vars_if_possible(found); let in_loop = self.is_loop(id) || self diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs index 2d64c5667a89..a0d31d3a22c3 100644 --- a/compiler/rustc_hir_typeck/src/method/suggest.rs +++ b/compiler/rustc_hir_typeck/src/method/suggest.rs @@ -2994,7 +2994,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // This occurs for UFCS desugaring of `T::method`, where there is no // receiver expression for the method call, and thus no autoderef. if let SelfSource::QPath(_) = source { - return is_local(self.resolve_vars_with_obligations(rcvr_ty)); + return is_local(rcvr_ty); } self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 07c559354c09..def9fdcd3c72 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -12,7 +12,7 @@ use rustc_data_structures::sync::{Lrc, OnceCell, WorkerLocal}; use rustc_errors::PResult; use rustc_expand::base::{ExtCtxt, LintStoreExpand}; use rustc_fs_util::try_canonicalize; -use rustc_hir::def_id::LOCAL_CRATE; +use rustc_hir::def_id::{StableCrateId, LOCAL_CRATE}; use rustc_lint::{unerased_lint_store, BufferedEarlyLint, EarlyCheckNode, LintStore}; use rustc_metadata::creader::CStore; use rustc_middle::arena::Arena; @@ -248,7 +248,7 @@ fn configure_and_expand( rustc_ast_passes::ast_validation::check_crate(sess, &krate, resolver.lint_buffer()) }); - let crate_types = sess.crate_types(); + let crate_types = tcx.crate_types(); let is_executable_crate = crate_types.contains(&CrateType::Executable); let is_proc_macro_crate = crate_types.contains(&CrateType::ProcMacro); @@ -340,11 +340,12 @@ fn early_lint_checks(tcx: TyCtxt<'_>, (): ()) { // Returns all the paths that correspond to generated files. fn generated_output_paths( - sess: &Session, + tcx: TyCtxt<'_>, outputs: &OutputFilenames, exact_name: bool, crate_name: Symbol, ) -> Vec { + let sess = tcx.sess; let mut out_filenames = Vec::new(); for output_type in sess.opts.output_types.keys() { let out_filename = outputs.path(*output_type); @@ -353,7 +354,7 @@ fn generated_output_paths( // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => { - for crate_type in sess.crate_types().iter() { + for crate_type in tcx.crate_types().iter() { let p = filename_for_input(sess, *crate_type, crate_name, outputs); out_filenames.push(p.as_path().to_path_buf()); } @@ -586,7 +587,7 @@ fn output_filenames(tcx: TyCtxt<'_>, (): ()) -> Arc { let outputs = util::build_output_filenames(&krate.attrs, sess); let output_paths = - generated_output_paths(sess, &outputs, sess.io.output_file.is_some(), crate_name); + generated_output_paths(tcx, &outputs, sess.io.output_file.is_some(), crate_name); // Ensure the source file isn't accidentally overwritten during compilation. if let Some(ref input_path) = sess.io.input.opt_path() { @@ -664,6 +665,8 @@ pub static DEFAULT_EXTERN_QUERY_PROVIDERS: LazyLock = LazyLock: pub fn create_global_ctxt<'tcx>( compiler: &'tcx Compiler, + crate_types: Vec, + stable_crate_id: StableCrateId, lint_store: Lrc, dep_graph: DepGraph, untracked: Untracked, @@ -696,6 +699,8 @@ pub fn create_global_ctxt<'tcx>( gcx_cell.get_or_init(move || { TyCtxt::create_global_ctxt( sess, + crate_types, + stable_crate_id, lint_store, arena, hir_arena, diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index b805ff09ee4c..7687e83da768 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -234,13 +234,13 @@ impl<'tcx> Queries<'tcx> { debug_assert_eq!(_id, CRATE_DEF_ID); let untracked = Untracked { cstore, source_span, definitions }; - // FIXME: Move these fields from session to tcx and make them immutable. - sess.init_crate_types(crate_types); - sess.stable_crate_id.set(stable_crate_id).expect("not yet initialized"); + // FIXME: Move features from session to tcx and make them immutable. sess.init_features(rustc_expand::config::features(sess, &pre_configured_attrs)); let qcx = passes::create_global_ctxt( self.compiler, + crate_types, + stable_crate_id, lint_store, self.dep_graph(dep_graph_future), untracked, @@ -320,7 +320,7 @@ impl<'tcx> Queries<'tcx> { let (crate_hash, prepare_outputs, dep_graph) = self.global_ctxt()?.enter(|tcx| { ( - if tcx.sess.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None }, + if tcx.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None }, tcx.output_filenames(()).clone(), tcx.dep_graph.clone(), ) diff --git a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp index 80b6c0fb4395..d61ec0b641c9 100644 --- a/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/CoverageMappingWrapper.cpp @@ -103,12 +103,20 @@ fromRust(LLVMRustCounterExprKind Kind) { } extern "C" void LLVMRustCoverageWriteFilenamesSectionToBuffer( - const char* const Filenames[], + const char *const Filenames[], size_t FilenamesLen, + const size_t *const Lengths, + size_t LengthsLen, RustStringRef BufferOut) { + if (FilenamesLen != LengthsLen) { + report_fatal_error( + "Mismatched lengths in LLVMRustCoverageWriteFilenamesSectionToBuffer"); + } + SmallVector FilenameRefs; + FilenameRefs.reserve(FilenamesLen); for (size_t i = 0; i < FilenamesLen; i++) { - FilenameRefs.push_back(std::string(Filenames[i])); + FilenameRefs.emplace_back(Filenames[i], Lengths[i]); } auto FilenamesWriter = coverage::CoverageFilenamesSectionWriter(ArrayRef(FilenameRefs)); @@ -153,8 +161,11 @@ extern "C" void LLVMRustCoverageWriteMappingToBuffer( CoverageMappingWriter.write(OS); } -extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar(LLVMValueRef F, const char *FuncName) { - StringRef FuncNameRef(FuncName); +extern "C" LLVMValueRef LLVMRustCoverageCreatePGOFuncNameVar( + LLVMValueRef F, + const char *FuncName, + size_t FuncNameLen) { + StringRef FuncNameRef(FuncName, FuncNameLen); return wrap(createPGOFuncNameVar(*cast(unwrap(F)), FuncNameRef)); } diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 7c9aaeb5762b..3c76f9a8e52c 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -545,7 +545,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { name, // The all loop is because `--crate-type=rlib --crate-type=rlib` is // legal and produces both inside this type. - self.sess.crate_types().iter().all(|c| *c == CrateType::Rlib), + self.tcx.crate_types().iter().all(|c| *c == CrateType::Rlib), hash, extra_filename, false, // is_host @@ -689,7 +689,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { fn inject_panic_runtime(&mut self, krate: &ast::Crate) { // If we're only compiling an rlib, then there's no need to select a // panic runtime, so we just skip this section entirely. - let any_non_rlib = self.sess.crate_types().iter().any(|ct| *ct != CrateType::Rlib); + let any_non_rlib = self.tcx.crate_types().iter().any(|ct| *ct != CrateType::Rlib); if !any_non_rlib { info!("panic runtime injection skipped, only generating rlib"); return; @@ -818,7 +818,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { // At this point we've determined that we need an allocator. Let's see // if our compilation session actually needs an allocator based on what // we're emitting. - let all_rlib = self.sess.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); + let all_rlib = self.tcx.crate_types().iter().all(|ct| matches!(*ct, CrateType::Rlib)); if all_rlib { return; } diff --git a/compiler/rustc_metadata/src/dependency_format.rs b/compiler/rustc_metadata/src/dependency_format.rs index 72b208a71327..783d35ac7e6f 100644 --- a/compiler/rustc_metadata/src/dependency_format.rs +++ b/compiler/rustc_metadata/src/dependency_format.rs @@ -66,8 +66,7 @@ use rustc_session::cstore::CrateDepKind; use rustc_session::cstore::LinkagePreference::{self, RequireDynamic, RequireStatic}; pub(crate) fn calculate(tcx: TyCtxt<'_>) -> Dependencies { - tcx.sess - .crate_types() + tcx.crate_types() .iter() .map(|&ty| { let linkage = calculate_type(tcx, ty); diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index 69a77e82f987..2a9662b809ae 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -56,7 +56,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // Always create a file at `metadata_filename`, even if we have nothing to write to it. // This simplifies the creation of the output `out_filename` when requested. - let metadata_kind = tcx.sess.metadata_kind(); + let metadata_kind = tcx.metadata_kind(); match metadata_kind { MetadataKind::None => { std::fs::File::create(&metadata_filename).unwrap_or_else(|err| { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index ca5043cc2633..098c411c8d67 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -52,10 +52,11 @@ fn find_bundled_library( verbatim: Option, kind: NativeLibKind, has_cfg: bool, - sess: &Session, + tcx: TyCtxt<'_>, ) -> Option { + let sess = tcx.sess; if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind - && sess.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib)) + && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::Staticlib)) && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) { let verbatim = verbatim.unwrap_or(false); @@ -364,7 +365,7 @@ impl<'tcx> Collector<'tcx> { }; let kind = kind.unwrap_or(NativeLibKind::Unspecified); - let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), sess); + let filename = find_bundled_library(name, verbatim, kind, cfg.is_some(), self.tcx); self.libs.push(NativeLib { name, filename, @@ -442,9 +443,13 @@ impl<'tcx> Collector<'tcx> { // Add if not found let new_name: Option<&str> = passed_lib.new_name.as_deref(); let name = Symbol::intern(new_name.unwrap_or(&passed_lib.name)); - let sess = self.tcx.sess; - let filename = - find_bundled_library(name, passed_lib.verbatim, passed_lib.kind, false, sess); + let filename = find_bundled_library( + name, + passed_lib.verbatim, + passed_lib.kind, + false, + self.tcx, + ); self.libs.push(NativeLib { name, filename, diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index b34fead821f9..be91ad4088a2 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1741,7 +1741,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { } fn encode_proc_macros(&mut self) -> Option { - let is_proc_macro = self.tcx.sess.crate_types().contains(&CrateType::ProcMacro); + let is_proc_macro = self.tcx.crate_types().contains(&CrateType::ProcMacro); if is_proc_macro { let tcx = self.tcx; let hir = tcx.hir(); @@ -2204,7 +2204,7 @@ fn encode_metadata_impl(tcx: TyCtxt<'_>, path: &Path) { source_file_cache, interpret_allocs: Default::default(), required_source_files, - is_proc_macro: tcx.sess.crate_types().contains(&CrateType::ProcMacro), + is_proc_macro: tcx.crate_types().contains(&CrateType::ProcMacro), hygiene_ctxt: &hygiene_ctxt, symbol_table: Default::default(), }; diff --git a/compiler/rustc_middle/src/dep_graph/dep_node.rs b/compiler/rustc_middle/src/dep_graph/dep_node.rs index 4e242c684e38..06251bccc985 100644 --- a/compiler/rustc_middle/src/dep_graph/dep_node.rs +++ b/compiler/rustc_middle/src/dep_graph/dep_node.rs @@ -60,7 +60,7 @@ use crate::mir::mono::MonoItem; use crate::ty::TyCtxt; use rustc_data_structures::fingerprint::Fingerprint; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; +use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::definitions::DefPathHash; use rustc_hir::{HirId, ItemLocalId, OwnerId}; use rustc_query_system::dep_graph::FingerprintStyle; @@ -371,7 +371,7 @@ impl<'tcx> DepNodeParams> for HirId { fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option { if tcx.fingerprint_style(dep_node.kind) == FingerprintStyle::HirId { let (local_hash, local_id) = Fingerprint::from(dep_node.hash).split(); - let def_path_hash = DefPathHash::new(tcx.sess.local_stable_crate_id(), local_hash); + let def_path_hash = DefPathHash::new(tcx.stable_crate_id(LOCAL_CRATE), local_hash); let def_id = tcx .def_path_hash_to_def_id(def_path_hash, &mut || { panic!("Failed to extract HirId: {:?} {}", dep_node.kind, dep_node.hash) diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs index fbc322638745..224f897492bb 100644 --- a/compiler/rustc_middle/src/hir/map/mod.rs +++ b/compiler/rustc_middle/src/hir/map/mod.rs @@ -1210,7 +1210,7 @@ pub(super) fn crate_hash(tcx: TyCtxt<'_>, _: LocalCrate) -> Svh { owner_spans.hash_stable(&mut hcx, &mut stable_hasher); } tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher); - tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher); + tcx.stable_crate_id(LOCAL_CRATE).hash_stable(&mut hcx, &mut stable_hasher); // Hash visibility information since it does not appear in HIR. resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher); resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher); diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index f19812619b21..e30b6b203d73 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -72,6 +72,6 @@ pub fn metadata_symbol_name(tcx: TyCtxt<'_>) -> String { format!( "rust_metadata_{}_{:08x}", tcx.crate_name(LOCAL_CRATE), - tcx.sess.local_stable_crate_id(), + tcx.stable_crate_id(LOCAL_CRATE), ) } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index ddb6cc15bc18..8fd980d5a9e9 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -524,13 +524,13 @@ impl<'tcx> CodegenUnitNameBuilder<'tcx> { // local crate's ID. Otherwise there can be collisions between CGUs // instantiating stuff for upstream crates. let local_crate_id = if cnum != LOCAL_CRATE { - let local_stable_crate_id = tcx.sess.local_stable_crate_id(); + let local_stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); format!("-in-{}.{:08x}", tcx.crate_name(LOCAL_CRATE), local_stable_crate_id) } else { String::new() }; - let stable_crate_id = tcx.sess.local_stable_crate_id(); + let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); format!("{}.{:08x}{}", tcx.crate_name(cnum), stable_crate_id, local_crate_id) }); diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 8ac1d02ce50e..be839e03cff6 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -59,8 +59,7 @@ use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::CrateType; use rustc_session::cstore::{CrateStoreDyn, Untracked}; use rustc_session::lint::Lint; -use rustc_session::Limit; -use rustc_session::Session; +use rustc_session::{Limit, MetadataKind, Session}; use rustc_span::def_id::{DefPathHash, StableCrateId}; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::{Span, DUMMY_SP}; @@ -527,6 +526,13 @@ pub struct GlobalCtxt<'tcx> { interners: CtxtInterners<'tcx>, pub sess: &'tcx Session, + crate_types: Vec, + /// The `stable_crate_id` is constructed out of the crate name and all the + /// `-C metadata` arguments passed to the compiler. Its value forms a unique + /// global identifier for the crate. It is used to allow multiple crates + /// with the same name to coexist. See the + /// `rustc_symbol_mangling` crate for more information. + stable_crate_id: StableCrateId, /// This only ever stores a `LintStore` but we don't want a dependency on that type here. /// @@ -687,6 +693,8 @@ impl<'tcx> TyCtxt<'tcx> { /// has a valid reference to the context, to allow formatting values that need it. pub fn create_global_ctxt( s: &'tcx Session, + crate_types: Vec, + stable_crate_id: StableCrateId, lint_store: Lrc, arena: &'tcx WorkerLocal>, hir_arena: &'tcx WorkerLocal>, @@ -705,6 +713,8 @@ impl<'tcx> TyCtxt<'tcx> { GlobalCtxt { sess: s, + crate_types, + stable_crate_id, lint_store, arena, hir_arena, @@ -800,10 +810,47 @@ impl<'tcx> TyCtxt<'tcx> { } } + #[inline] + pub fn crate_types(self) -> &'tcx [CrateType] { + &self.crate_types + } + + pub fn metadata_kind(self) -> MetadataKind { + self.crate_types() + .iter() + .map(|ty| match *ty { + CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => { + MetadataKind::None + } + CrateType::Rlib => MetadataKind::Uncompressed, + CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, + }) + .max() + .unwrap_or(MetadataKind::None) + } + + pub fn needs_metadata(self) -> bool { + self.metadata_kind() != MetadataKind::None + } + + pub fn needs_crate_hash(self) -> bool { + // Why is the crate hash needed for these configurations? + // - debug_assertions: for the "fingerprint the result" check in + // `rustc_query_system::query::plumbing::execute_job`. + // - incremental: for query lookups. + // - needs_metadata: for putting into crate metadata. + // - instrument_coverage: for putting into coverage data (see + // `hash_mir_source`). + cfg!(debug_assertions) + || self.sess.opts.incremental.is_some() + || self.needs_metadata() + || self.sess.instrument_coverage() + } + #[inline] pub fn stable_crate_id(self, crate_num: CrateNum) -> StableCrateId { if crate_num == LOCAL_CRATE { - self.sess.local_stable_crate_id() + self.stable_crate_id } else { self.cstore_untracked().stable_crate_id(crate_num) } @@ -813,7 +860,7 @@ impl<'tcx> TyCtxt<'tcx> { /// that the crate in question has already been loaded by the CrateStore. #[inline] pub fn stable_crate_id_to_crate_num(self, stable_crate_id: StableCrateId) -> CrateNum { - if stable_crate_id == self.sess.local_stable_crate_id() { + if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { LOCAL_CRATE } else { self.cstore_untracked().stable_crate_id_to_crate_num(stable_crate_id) @@ -830,7 +877,7 @@ impl<'tcx> TyCtxt<'tcx> { // If this is a DefPathHash from the local crate, we can look up the // DefId in the tcx's `Definitions`. - if stable_crate_id == self.sess.local_stable_crate_id() { + if stable_crate_id == self.stable_crate_id(LOCAL_CRATE) { self.untracked.definitions.read().local_def_path_hash_to_def_id(hash, err).to_def_id() } else { // If this is a DefPathHash from an upstream crate, let the CrateStore map @@ -847,7 +894,7 @@ impl<'tcx> TyCtxt<'tcx> { // statements within the query system and we'd run into endless // recursion otherwise. let (crate_name, stable_crate_id) = if def_id.is_local() { - (self.crate_name(LOCAL_CRATE), self.sess.local_stable_crate_id()) + (self.crate_name(LOCAL_CRATE), self.stable_crate_id(LOCAL_CRATE)) } else { let cstore = &*self.cstore_untracked(); (cstore.crate_name(def_id.krate), cstore.stable_crate_id(def_id.krate)) @@ -986,7 +1033,7 @@ impl<'tcx> TyCtxt<'tcx> { pub fn local_crate_exports_generics(self) -> bool { debug_assert!(self.sess.opts.share_generics()); - self.sess.crate_types().iter().any(|crate_type| { + self.crate_types().iter().any(|crate_type| { match crate_type { CrateType::Executable | CrateType::Staticlib diff --git a/compiler/rustc_passes/src/entry.rs b/compiler/rustc_passes/src/entry.rs index ffd8f77b78b8..6a29159e9178 100644 --- a/compiler/rustc_passes/src/entry.rs +++ b/compiler/rustc_passes/src/entry.rs @@ -31,7 +31,7 @@ struct EntryContext<'tcx> { } fn entry_fn(tcx: TyCtxt<'_>, (): ()) -> Option<(DefId, EntryFnType)> { - let any_exe = tcx.sess.crate_types().iter().any(|ty| *ty == CrateType::Executable); + let any_exe = tcx.crate_types().iter().any(|ty| *ty == CrateType::Executable); if !any_exe { // No need to find a main function. return None; diff --git a/compiler/rustc_passes/src/reachable.rs b/compiler/rustc_passes/src/reachable.rs index 7dec5b0acc88..f9d34ea71baf 100644 --- a/compiler/rustc_passes/src/reachable.rs +++ b/compiler/rustc_passes/src/reachable.rs @@ -364,10 +364,10 @@ fn has_custom_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { fn reachable_set(tcx: TyCtxt<'_>, (): ()) -> LocalDefIdSet { let effective_visibilities = &tcx.effective_visibilities(()); - let any_library = - tcx.sess.crate_types().iter().any(|ty| { - *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro - }); + let any_library = tcx + .crate_types() + .iter() + .any(|ty| *ty == CrateType::Rlib || *ty == CrateType::Dylib || *ty == CrateType::ProcMacro); let mut reachable_context = ReachableContext { tcx, maybe_typeck_results: None, diff --git a/compiler/rustc_passes/src/weak_lang_items.rs b/compiler/rustc_passes/src/weak_lang_items.rs index fc6372cf99ee..75e071f1fcfe 100644 --- a/compiler/rustc_passes/src/weak_lang_items.rs +++ b/compiler/rustc_passes/src/weak_lang_items.rs @@ -43,7 +43,7 @@ pub fn check_crate(tcx: TyCtxt<'_>, items: &mut lang_items::LanguageItems) { fn verify(tcx: TyCtxt<'_>, items: &lang_items::LanguageItems) { // We only need to check for the presence of weak lang items if we're // emitting something that's not an rlib. - let needs_check = tcx.sess.crate_types().iter().any(|kind| match *kind { + let needs_check = tcx.crate_types().iter().any(|kind| match *kind { CrateType::Dylib | CrateType::ProcMacro | CrateType::Cdylib diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 6007295b9305..c87db96a5dd9 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -4374,7 +4374,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { if let Some(res) = res && let Some(def_id) = res.opt_def_id() && !def_id.is_local() - && self.r.tcx.sess.crate_types().contains(&CrateType::ProcMacro) + && self.r.tcx.crate_types().contains(&CrateType::ProcMacro) && matches!(self.r.tcx.sess.opts.resolve_doc_links, ResolveDocLinks::ExportedMetadata) { // Encoding foreign def ids in proc macro crate metadata will ICE. return None; @@ -4389,7 +4389,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { match self.r.tcx.sess.opts.resolve_doc_links { ResolveDocLinks::None => return, ResolveDocLinks::ExportedMetadata - if !self.r.tcx.sess.crate_types().iter().copied().any(CrateType::has_metadata) + if !self.r.tcx.crate_types().iter().copied().any(CrateType::has_metadata) || !maybe_exported.eval(self.r) => { return; @@ -4448,7 +4448,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> { .into_iter() .filter_map(|tr| { if !tr.def_id.is_local() - && self.r.tcx.sess.crate_types().contains(&CrateType::ProcMacro) + && self.r.tcx.crate_types().contains(&CrateType::ProcMacro) && matches!( self.r.tcx.sess.opts.resolve_doc_links, ResolveDocLinks::ExportedMetadata diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b25a62991255..5d7572f65a55 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -152,14 +152,6 @@ pub struct Session { /// Input, input file path and output file path to this compilation process. pub io: CompilerIO, - crate_types: OnceCell>, - /// The `stable_crate_id` is constructed out of the crate name and all the - /// `-C metadata` arguments passed to the compiler. Its value forms a unique - /// global identifier for the crate. It is used to allow multiple crates - /// with the same name to coexist. See the - /// `rustc_symbol_mangling` crate for more information. - pub stable_crate_id: OnceCell, - features: OnceCell, incr_comp_session: OneThread>, @@ -310,55 +302,11 @@ impl Session { self.parse_sess.span_diagnostic.emit_future_breakage_report(diags); } - pub fn local_stable_crate_id(&self) -> StableCrateId { - self.stable_crate_id.get().copied().unwrap() - } - - pub fn crate_types(&self) -> &[CrateType] { - self.crate_types.get().unwrap().as_slice() - } - /// Returns true if the crate is a testing one. pub fn is_test_crate(&self) -> bool { self.opts.test } - pub fn needs_crate_hash(&self) -> bool { - // Why is the crate hash needed for these configurations? - // - debug_assertions: for the "fingerprint the result" check in - // `rustc_query_system::query::plumbing::execute_job`. - // - incremental: for query lookups. - // - needs_metadata: for putting into crate metadata. - // - instrument_coverage: for putting into coverage data (see - // `hash_mir_source`). - cfg!(debug_assertions) - || self.opts.incremental.is_some() - || self.needs_metadata() - || self.instrument_coverage() - } - - pub fn metadata_kind(&self) -> MetadataKind { - self.crate_types() - .iter() - .map(|ty| match *ty { - CrateType::Executable | CrateType::Staticlib | CrateType::Cdylib => { - MetadataKind::None - } - CrateType::Rlib => MetadataKind::Uncompressed, - CrateType::Dylib | CrateType::ProcMacro => MetadataKind::Compressed, - }) - .max() - .unwrap_or(MetadataKind::None) - } - - pub fn needs_metadata(&self) -> bool { - self.metadata_kind() != MetadataKind::None - } - - pub fn init_crate_types(&self, crate_types: Vec) { - self.crate_types.set(crate_types).expect("`crate_types` was initialized twice") - } - #[rustc_lint_diagnostics] #[track_caller] pub fn struct_span_warn>( @@ -1516,8 +1464,6 @@ pub fn build_session( parse_sess, sysroot, io, - crate_types: OnceCell::new(), - stable_crate_id: OnceCell::new(), features: OnceCell::new(), incr_comp_session: OneThread::new(RefCell::new(IncrCompSession::NotInitialized)), cgu_reuse_tracker, diff --git a/compiler/rustc_smir/src/rustc_internal/mod.rs b/compiler/rustc_smir/src/rustc_internal/mod.rs index 0d9157940f82..078ff67446f6 100644 --- a/compiler/rustc_smir/src/rustc_internal/mod.rs +++ b/compiler/rustc_smir/src/rustc_internal/mod.rs @@ -63,6 +63,10 @@ pub fn trait_def(did: DefId) -> stable_mir::ty::TraitDef { with_tables(|t| t.trait_def(did)) } +pub fn impl_def(did: DefId) -> stable_mir::ty::ImplDef { + with_tables(|t| t.impl_def(did)) +} + impl<'tcx> Tables<'tcx> { pub fn item_def_id(&self, item: &stable_mir::CrateItem) -> DefId { self.def_ids[item.0] @@ -72,6 +76,10 @@ impl<'tcx> Tables<'tcx> { self.def_ids[trait_def.0] } + pub fn impl_trait_def_id(&self, impl_def: &stable_mir::ty::ImplDef) -> DefId { + self.def_ids[impl_def.0] + } + pub fn crate_item(&mut self, did: DefId) -> stable_mir::CrateItem { stable_mir::CrateItem(self.create_def_id(did)) } @@ -116,6 +124,10 @@ impl<'tcx> Tables<'tcx> { stable_mir::ty::ConstDef(self.create_def_id(did)) } + pub fn impl_def(&mut self, did: DefId) -> stable_mir::ty::ImplDef { + stable_mir::ty::ImplDef(self.create_def_id(did)) + } + fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId { // FIXME: this becomes inefficient when we have too many ids for (i, &d) in self.def_ids.iter().enumerate() { diff --git a/compiler/rustc_smir/src/rustc_smir/mod.rs b/compiler/rustc_smir/src/rustc_smir/mod.rs index f782138f8705..6ab473c3b54c 100644 --- a/compiler/rustc_smir/src/rustc_smir/mod.rs +++ b/compiler/rustc_smir/src/rustc_smir/mod.rs @@ -59,6 +59,20 @@ impl<'tcx> Context for Tables<'tcx> { trait_def.stable(self) } + fn all_trait_impls(&mut self) -> stable_mir::ImplTraitDecls { + self.tcx + .trait_impls_in_crate(LOCAL_CRATE) + .iter() + .map(|impl_def_id| self.impl_def(*impl_def_id)) + .collect() + } + + fn trait_impl(&mut self, impl_def: &stable_mir::ty::ImplDef) -> stable_mir::ty::ImplTrait { + let def_id = self.impl_trait_def_id(impl_def); + let impl_trait = self.tcx.impl_trait_ref(def_id).unwrap(); + impl_trait.stable(self) + } + fn mir_body(&mut self, item: &stable_mir::CrateItem) -> stable_mir::mir::Body { let def_id = self.item_def_id(item); let mir = self.tcx.optimized_mir(def_id); @@ -840,6 +854,19 @@ where } } +impl<'tcx, S, V> Stable<'tcx> for ty::EarlyBinder +where + S: Stable<'tcx, T = V>, +{ + type T = stable_mir::ty::EarlyBinder; + + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::EarlyBinder; + + EarlyBinder { value: self.as_ref().skip_binder().stable(tables) } + } +} + impl<'tcx> Stable<'tcx> for ty::FnSig<'tcx> { type T = stable_mir::ty::FnSig; fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { @@ -1154,3 +1181,12 @@ impl<'tcx> Stable<'tcx> for rustc_middle::mir::ConstantKind<'tcx> { } } } + +impl<'tcx> Stable<'tcx> for ty::TraitRef<'tcx> { + type T = stable_mir::ty::TraitRef; + fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T { + use stable_mir::ty::TraitRef; + + TraitRef { def_id: rustc_internal::trait_def(self.def_id), args: self.args.stable(tables) } + } +} diff --git a/compiler/rustc_smir/src/stable_mir/mod.rs b/compiler/rustc_smir/src/stable_mir/mod.rs index d93f25249b9e..a74975cefc2c 100644 --- a/compiler/rustc_smir/src/stable_mir/mod.rs +++ b/compiler/rustc_smir/src/stable_mir/mod.rs @@ -15,7 +15,7 @@ use std::cell::Cell; use crate::rustc_smir::Tables; -use self::ty::{TraitDecl, TraitDef, Ty, TyKind}; +use self::ty::{ImplDef, ImplTrait, TraitDecl, TraitDef, Ty, TyKind}; pub mod mir; pub mod ty; @@ -32,9 +32,12 @@ pub type DefId = usize; /// A list of crate items. pub type CrateItems = Vec; -/// A list of crate items. +/// A list of trait decls. pub type TraitDecls = Vec; +/// A list of impl trait decls. +pub type ImplTraitDecls = Vec; + /// Holds information about a crate. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Crate { @@ -89,6 +92,8 @@ pub trait Context { fn mir_body(&mut self, item: &CrateItem) -> mir::Body; fn all_trait_decls(&mut self) -> TraitDecls; fn trait_decl(&mut self, trait_def: &TraitDef) -> TraitDecl; + fn all_trait_impls(&mut self) -> ImplTraitDecls; + fn trait_impl(&mut self, trait_impl: &ImplDef) -> ImplTrait; /// Get information about the local crate. fn local_crate(&self) -> Crate; /// Retrieve a list of all external crates. diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index e83ca88da1b6..8a23af75749a 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -119,6 +119,15 @@ impl TraitDef { } } +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ImplDef(pub(crate) DefId); + +impl ImplDef { + pub fn trait_impl(&self) -> ImplTrait { + with(|cx| cx.trait_impl(self)) + } +} + #[derive(Clone, Debug)] pub struct GenericArgs(pub Vec); @@ -196,6 +205,11 @@ pub struct Binder { pub bound_vars: Vec, } +#[derive(Clone, Debug)] +pub struct EarlyBinder { + pub value: T, +} + #[derive(Clone, Debug)] pub enum BoundVariableKind { Ty(BoundTyKind), @@ -432,3 +446,10 @@ pub struct TraitDecl { pub implement_via_object: bool, pub deny_explicit_impl: bool, } + +pub type ImplTrait = EarlyBinder; + +pub struct TraitRef { + pub def_id: TraitDef, + pub args: GenericArgs, +} diff --git a/compiler/rustc_symbol_mangling/src/lib.rs b/compiler/rustc_symbol_mangling/src/lib.rs index 22fd1f09f3a3..74538e9f5a3f 100644 --- a/compiler/rustc_symbol_mangling/src/lib.rs +++ b/compiler/rustc_symbol_mangling/src/lib.rs @@ -180,7 +180,7 @@ fn compute_symbol_name<'tcx>( if let Some(def_id) = def_id.as_local() { if tcx.proc_macro_decls_static(()) == Some(def_id) { - let stable_crate_id = tcx.sess.local_stable_crate_id(); + let stable_crate_id = tcx.stable_crate_id(LOCAL_CRATE); return tcx.sess.generate_proc_macro_decls_symbol(stable_crate_id); } } diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs index 845b57911618..d345368d552b 100644 --- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs +++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs @@ -824,11 +824,8 @@ fn transform_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, options: TransformTyOptio } ty::Array(ty0, len) => { - let len = len - .try_to_scalar() - .unwrap() - .to_u64() - .unwrap_or_else(|_| panic!("failed to convert length to u64")); + let len = len.eval_target_usize(tcx, ty::ParamEnv::reveal_all()); + ty = Ty::new_array(tcx, transform_ty(tcx, *ty0, options), len); } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs index 56f126e91572..be48447e27c2 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/cache.rs @@ -19,21 +19,25 @@ rustc_index::newtype_index! { #[derive(Debug, Clone)] pub(super) struct ProvisionalEntry<'tcx> { - // In case we have a coinductive cycle, this is the - // the currently least restrictive result of this goal. - pub(super) response: QueryResult<'tcx>, - // In case of a cycle, the position of deepest stack entry involved - // in that cycle. This is monotonically decreasing in the stack as all - // elements between the current stack element in the deepest stack entry - // involved have to also be involved in that cycle. - // - // We can only move entries to the global cache once we're complete done - // with the cycle. If this entry has not been involved in a cycle, - // this is just its own depth. + /// In case we have a coinductive cycle, this is the + /// the current provisional result of this goal. + /// + /// This starts out as `None` for all goals and gets to some + /// when the goal gets popped from the stack or we rerun evaluation + /// for this goal to reach a fixpoint. + pub(super) response: Option>, + /// In case of a cycle, the position of deepest stack entry involved + /// in that cycle. This is monotonically decreasing in the stack as all + /// elements between the current stack element in the deepest stack entry + /// involved have to also be involved in that cycle. + /// + /// We can only move entries to the global cache once we're complete done + /// with the cycle. If this entry has not been involved in a cycle, + /// this is just its own depth. pub(super) depth: StackDepth, - // The goal for this entry. Should always be equal to the corresponding goal - // in the lookup table. + /// The goal for this entry. Should always be equal to the corresponding goal + /// in the lookup table. pub(super) input: CanonicalInput<'tcx>, } @@ -92,7 +96,7 @@ impl<'tcx> ProvisionalCache<'tcx> { self.entries[entry_index].depth } - pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> { + pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> Option> { self.entries[entry_index].response } } diff --git a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs index 21c8d476902a..b860f374c0ab 100644 --- a/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/search_graph/mod.rs @@ -13,7 +13,7 @@ use rustc_middle::traits::solve::CacheData; use rustc_middle::traits::solve::{CanonicalInput, Certainty, EvaluationCache, QueryResult}; use rustc_middle::ty::TyCtxt; use rustc_session::Limit; -use std::{collections::hash_map::Entry, mem}; +use std::collections::hash_map::Entry; rustc_index::newtype_index! { pub struct StackDepth {} @@ -216,8 +216,8 @@ impl<'tcx> SearchGraph<'tcx> { cycle_participants: Default::default(), }; assert_eq!(self.stack.push(entry), depth); - let response = Self::response_no_constraints(tcx, input, Certainty::Yes); - let entry_index = cache.entries.push(ProvisionalEntry { response, depth, input }); + let entry_index = + cache.entries.push(ProvisionalEntry { response: None, depth, input }); v.insert(entry_index); } // We have a nested goal which relies on a goal `root` deeper in the stack. @@ -243,23 +243,31 @@ impl<'tcx> SearchGraph<'tcx> { root.cycle_participants.insert(e.input); } - // NOTE: The goals on the stack aren't the only goals involved in this cycle. - // We can also depend on goals which aren't part of the stack but coinductively - // depend on the stack themselves. We already checked whether all the goals - // between these goals and their root on the stack. This means that as long as - // each goal in a cycle is checked for coinductivity by itself, simply checking - // the stack is enough. - if self.stack.raw[stack_depth.index()..] - .iter() - .all(|g| g.input.value.goal.predicate.is_coinductive(tcx)) - { - // If we're in a coinductive cycle, we have to retry proving the current goal - // until we reach a fixpoint. - self.stack[stack_depth].has_been_used = true; - return cache.provisional_result(entry_index); + // If we're in a cycle, we have to retry proving the current goal + // until we reach a fixpoint. + self.stack[stack_depth].has_been_used = true; + return if let Some(result) = cache.provisional_result(entry_index) { + result } else { - return Self::response_no_constraints(tcx, input, Certainty::OVERFLOW); - } + // If we don't have a provisional result yet, the goal has to + // still be on the stack. + let mut goal_on_stack = false; + let mut is_coinductive = true; + for entry in self.stack.raw[stack_depth.index()..] + .iter() + .skip_while(|entry| entry.input != input) + { + goal_on_stack = true; + is_coinductive &= entry.input.value.goal.predicate.is_coinductive(tcx); + } + debug_assert!(goal_on_stack); + + if is_coinductive { + Self::response_no_constraints(tcx, input, Certainty::Yes) + } else { + Self::response_no_constraints(tcx, input, Certainty::OVERFLOW) + } + }; } } @@ -288,15 +296,18 @@ impl<'tcx> SearchGraph<'tcx> { let provisional_entry_index = *cache.lookup_table.get(&stack_entry.input).unwrap(); let provisional_entry = &mut cache.entries[provisional_entry_index]; - let prev_response = mem::replace(&mut provisional_entry.response, response); - if stack_entry.has_been_used && prev_response != response { - // If so, remove all entries whose result depends on this goal - // from the provisional cache... + if stack_entry.has_been_used + && provisional_entry.response.map_or(true, |r| r != response) + { + // If so, update the provisional result for this goal and remove + // all entries whose result depends on this goal from the provisional + // cache... // - // That's not completely correct, as a nested goal can also + // That's not completely correct, as a nested goal can also only // depend on a goal which is lower in the stack so it doesn't // actually depend on the current goal. This should be fairly // rare and is hopefully not relevant for performance. + provisional_entry.response = Some(response); #[allow(rustc::potential_query_instability)] cache.lookup_table.retain(|_key, index| *index <= provisional_entry_index); cache.entries.truncate(provisional_entry_index.index() + 1); @@ -315,8 +326,8 @@ 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 do not add it to the global - // cache. + // 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. @@ -348,6 +359,8 @@ impl<'tcx> SearchGraph<'tcx> { dep_node, result, ) + } else { + provisional_entry.response = Some(result); } result diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index f1bd9f8b71a7..4371b6c1239e 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -2394,7 +2394,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> { Ok(args) => args, Err(()) => { // FIXME: A rematch may fail when a candidate cache hit occurs - // on thefreshened form of the trait predicate, but the match + // on the freshened form of the trait predicate, but the match // fails for some reason that is not captured in the freshened // cache key. For example, equating an impl trait ref against // the placeholder trait ref may fail due the Generalizer relation diff --git a/compiler/rustc_type_ir/src/sty.rs b/compiler/rustc_type_ir/src/sty.rs index ec0dbffc22f2..72bd50ace6de 100644 --- a/compiler/rustc_type_ir/src/sty.rs +++ b/compiler/rustc_type_ir/src/sty.rs @@ -200,7 +200,9 @@ pub enum TyKind { /// A tuple type. For example, `(i32, bool)`. Tuple(I::ListTy), - /// A projection or opaque type. Both of these types + /// A projection, opaque type, weak type alias, or inherent associated type. + /// All of these types are represented as pairs of def-id and args, and can + /// be normalized, so they are grouped conceptually. Alias(AliasKind, I::AliasTy), /// A type parameter; for example, `T` in `fn f(x: T) {}`. diff --git a/library/alloc/src/collections/linked_list.rs b/library/alloc/src/collections/linked_list.rs index 052edf453f67..2c26f9e03124 100644 --- a/library/alloc/src/collections/linked_list.rs +++ b/library/alloc/src/collections/linked_list.rs @@ -18,7 +18,7 @@ use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::PhantomData; use core::mem; -use core::ptr::{NonNull, Unique}; +use core::ptr::NonNull; use super::SpecExtend; use crate::alloc::{Allocator, Global}; @@ -168,15 +168,16 @@ impl LinkedList { /// Adds the given node to the front of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed using the list's allocator. + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. #[inline] - unsafe fn push_front_node(&mut self, node: Unique>) { + unsafe fn push_front_node(&mut self, node: NonNull>) { // This method takes care not to create mutable references to whole nodes, // to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = self.head; (*node.as_ptr()).prev = None; - let node = Some(NonNull::from(node)); + let node = Some(node); match self.head { None => self.tail = node, @@ -212,15 +213,16 @@ impl LinkedList { /// Adds the given node to the back of the list. /// /// # Safety - /// `node` must point to a valid node that was boxed using the list's allocator. + /// `node` must point to a valid node that was boxed and leaked using the list's allocator. + /// This method takes ownership of the node, so the pointer should not be used again. #[inline] - unsafe fn push_back_node(&mut self, node: Unique>) { + unsafe fn push_back_node(&mut self, node: NonNull>) { // This method takes care not to create mutable references to whole nodes, // to maintain validity of aliasing pointers into `element`. unsafe { (*node.as_ptr()).next = None; (*node.as_ptr()).prev = self.tail; - let node = Some(NonNull::from(node)); + let node = Some(node); match self.tail { None => self.head = node, @@ -842,8 +844,8 @@ impl LinkedList { #[stable(feature = "rust1", since = "1.0.0")] pub fn push_front(&mut self, elt: T) { let node = Box::new_in(Node::new(elt), &self.alloc); - let node_ptr = Unique::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked unsafe { self.push_front_node(node_ptr); } @@ -890,8 +892,8 @@ impl LinkedList { #[stable(feature = "rust1", since = "1.0.0")] pub fn push_back(&mut self, elt: T) { let node = Box::new_in(Node::new(elt), &self.alloc); - let node_ptr = Unique::from(Box::leak(node)); - // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc + let node_ptr = NonNull::from(Box::leak(node)); + // SAFETY: node_ptr is a unique pointer to a node we boxed with self.alloc and leaked unsafe { self.push_back_node(node_ptr); } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 60b07485c3a8..afed3fdf7459 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2491,9 +2491,9 @@ impl From> for Rc<[T], A> { /// /// ``` /// # use std::rc::Rc; - /// let original: Box> = Box::new(vec![1, 2, 3]); - /// let shared: Rc> = Rc::from(original); - /// assert_eq!(vec![1, 2, 3], *shared); + /// let unique: Vec = vec![1, 2, 3]; + /// let shared: Rc<[i32]> = Rc::from(unique); + /// assert_eq!(&[1, 2, 3], &shared[..]); /// ``` #[inline] fn from(v: Vec) -> Rc<[T], A> { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 74046a9c7c38..502f8a75863a 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1,7 +1,7 @@ use super::*; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{self, const_eval_select}; -use crate::mem; +use crate::mem::{self, SizedTypeProperties}; use crate::slice::{self, SliceIndex}; impl *const T { @@ -995,14 +995,23 @@ impl *const T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + // We could always go back to wrapping if unchecked becomes unacceptable + #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { self.offset((count as isize).wrapping_neg()) } + if T::IS_ZST { + // Pointer arithmetic does nothing when the pointee is a ZST. + self + } else { + // SAFETY: the caller must uphold the safety contract for `offset`. + // Because the pointee is *not* a ZST, that means that `count` is + // at most `isize::MAX`, and thus the negation cannot overflow. + unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + } } /// Calculates the offset from a pointer in bytes (convenience for diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index e3a3f69afd9a..d129e1d645f5 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1,6 +1,7 @@ use super::*; use crate::cmp::Ordering::{self, Equal, Greater, Less}; use crate::intrinsics::{self, const_eval_select}; +use crate::mem::SizedTypeProperties; use crate::slice::{self, SliceIndex}; impl *mut T { @@ -1095,14 +1096,23 @@ impl *mut T { #[stable(feature = "pointer_methods", since = "1.26.0")] #[must_use = "returns a new pointer rather than modifying its argument"] #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")] + // We could always go back to wrapping if unchecked becomes unacceptable + #[rustc_allow_const_fn_unstable(const_int_unchecked_arith)] #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn sub(self, count: usize) -> Self where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `offset`. - unsafe { self.offset((count as isize).wrapping_neg()) } + if T::IS_ZST { + // Pointer arithmetic does nothing when the pointee is a ZST. + self + } else { + // SAFETY: the caller must uphold the safety contract for `offset`. + // Because the pointee is *not* a ZST, that means that `count` is + // at most `isize::MAX`, and thus the negation cannot overflow. + unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) } + } } /// Calculates the offset from a pointer in bytes (convenience for diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index b871be733b85..5b213555394d 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -25,14 +25,12 @@ hashbrown = { version = "0.14", default-features = false, features = ['rustc-dep std_detect = { path = "../stdarch/crates/std_detect", default-features = false, features = ['rustc-dep-of-std'] } # Dependencies of the `backtrace` crate -addr2line = { version = "0.20.0", optional = true, default-features = false } rustc-demangle = { version = "0.1.21", features = ['rustc-dep-of-std'] } -miniz_oxide = { version = "0.7.0", optional = true, default-features = false, public = false } -[dependencies.object] -version = "0.31.1" -optional = true -default-features = false -features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] + +[target.'cfg(not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))))'.dependencies] +miniz_oxide = { version = "0.7.0", optional = true, default-features = false } +addr2line = { version = "0.20.0", optional = true, default-features = false } +object = { version = "0.31.1", default-features = false, optional = true, features = ['read_core', 'elf', 'macho', 'pe', 'unaligned', 'archive'] } [dev-dependencies] rand = { version = "0.8.5", default-features = false, features = ["alloc"] } diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 0f79e74f5555..a7e65305386e 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -791,6 +791,7 @@ impl Write for &File { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { self.inner.flush() } @@ -836,6 +837,7 @@ impl Write for File { fn is_write_vectored(&self) -> bool { (&&*self).is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { (&*self).flush() } @@ -881,6 +883,7 @@ impl Write for Arc { fn is_write_vectored(&self) -> bool { (&**self).is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { (&**self).flush() } diff --git a/library/std/src/io/readbuf.rs b/library/std/src/io/readbuf.rs index 1f3f80b9618e..034ddd8df9ae 100644 --- a/library/std/src/io/readbuf.rs +++ b/library/std/src/io/readbuf.rs @@ -310,6 +310,7 @@ impl<'a> Write for BorrowedCursor<'a> { Ok(buf.len()) } + #[inline] fn flush(&mut self) -> Result<()> { Ok(()) } diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f08ffa757002..9038e8fa9d7a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -397,9 +397,15 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; +// FIXME: #94122 this extern crate definition only exist here to stop +// miniz_oxide docs leaking into std docs. Find better way to do it. +// Remove exclusion from tidy platform check when this removed. #[doc(masked)] #[allow(unused_extern_crates)] -#[cfg(feature = "miniz_oxide")] +#[cfg(all( + not(all(windows, target_env = "msvc", not(target_vendor = "uwp"))), + feature = "miniz_oxide" +))] extern crate miniz_oxide; // During testing, this crate is not actually the "real" std library, but rather diff --git a/library/std/src/net/tcp.rs b/library/std/src/net/tcp.rs index 141a18a42dde..32fd54c8e751 100644 --- a/library/std/src/net/tcp.rs +++ b/library/std/src/net/tcp.rs @@ -647,6 +647,7 @@ impl Write for TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -685,6 +686,7 @@ impl Write for &TcpStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/os/unix/net/stream.rs b/library/std/src/os/unix/net/stream.rs index e20170873bbb..41290e0017ac 100644 --- a/library/std/src/os/unix/net/stream.rs +++ b/library/std/src/os/unix/net/stream.rs @@ -712,6 +712,7 @@ impl<'a> io::Write for &'a UnixStream { self.0.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/process.rs b/library/std/src/process.rs index f1305dee1461..f25ad2ece714 100644 --- a/library/std/src/process.rs +++ b/library/std/src/process.rs @@ -280,6 +280,7 @@ impl Write for ChildStdin { io::Write::is_write_vectored(&&*self) } + #[inline] fn flush(&mut self) -> io::Result<()> { (&*self).flush() } @@ -299,6 +300,7 @@ impl Write for &ChildStdin { self.inner.is_write_vectored() } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sync/barrier.rs b/library/std/src/sync/barrier.rs index e39254aa4349..ed3c55120847 100644 --- a/library/std/src/sync/barrier.rs +++ b/library/std/src/sync/barrier.rs @@ -130,11 +130,8 @@ impl Barrier { let local_gen = lock.generation_id; lock.count += 1; if lock.count < self.num_threads { - // We need a while loop to guard against spurious wakeups. - // https://en.wikipedia.org/wiki/Spurious_wakeup - while local_gen == lock.generation_id { - lock = self.cvar.wait(lock).unwrap(); - } + let _guard = + self.cvar.wait_while(lock, |state| local_gen == state.generation_id).unwrap(); BarrierWaitResult(false) } else { lock.count = 0; diff --git a/library/std/src/sys/hermit/fs.rs b/library/std/src/sys/hermit/fs.rs index 4bb735668d24..6aa4ea7f5b4f 100644 --- a/library/std/src/sys/hermit/fs.rs +++ b/library/std/src/sys/hermit/fs.rs @@ -335,6 +335,7 @@ impl File { false } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unix/fs.rs b/library/std/src/sys/unix/fs.rs index 7dc809a038b3..a5604c92a80b 100644 --- a/library/std/src/sys/unix/fs.rs +++ b/library/std/src/sys/unix/fs.rs @@ -1204,6 +1204,7 @@ impl File { self.0.write_vectored_at(bufs, offset) } + #[inline] pub fn flush(&self) -> io::Result<()> { Ok(()) } diff --git a/library/std/src/sys/unix/stdio.rs b/library/std/src/sys/unix/stdio.rs index a26f20795a19..97e75f1b5b66 100644 --- a/library/std/src/sys/unix/stdio.rs +++ b/library/std/src/sys/unix/stdio.rs @@ -54,6 +54,7 @@ impl io::Write for Stdout { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } @@ -81,6 +82,7 @@ impl io::Write for Stderr { true } + #[inline] fn flush(&mut self) -> io::Result<()> { Ok(()) } diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 62c7b96b5be9..cad370ad7565 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -1129,7 +1129,7 @@ fn needs_codegen_config(run: &RunConfig<'_>) -> bool { needs_codegen_cfg } -const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; +pub(crate) const CODEGEN_BACKEND_PREFIX: &str = "rustc_codegen_"; fn is_codegen_cfg_needed(path: &TaskPath, run: &RunConfig<'_>) -> bool { if path.path.to_str().unwrap().contains(&CODEGEN_BACKEND_PREFIX) { diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index cdb34f991648..830c1a3b8468 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -20,6 +20,7 @@ use std::str::FromStr; use crate::cache::{Interned, INTERNER}; use crate::cc_detect::{ndk_compiler, Language}; use crate::channel::{self, GitInfo}; +use crate::compile::CODEGEN_BACKEND_PREFIX; pub use crate::flags::Subcommand; use crate::flags::{Color, Flags, Warnings}; use crate::util::{exe, output, t}; @@ -1443,8 +1444,21 @@ impl Config { .map(|v| v.parse().expect("failed to parse rust.llvm-libunwind")); if let Some(ref backends) = rust.codegen_backends { - config.rust_codegen_backends = - backends.iter().map(|s| INTERNER.intern_str(s)).collect(); + let available_backends = vec!["llvm", "cranelift", "gcc"]; + + config.rust_codegen_backends = backends.iter().map(|s| { + if let Some(backend) = s.strip_prefix(CODEGEN_BACKEND_PREFIX) { + if available_backends.contains(&backend) { + panic!("Invalid value '{s}' for 'rust.codegen-backends'. Instead, please use '{backend}'."); + } else { + println!("help: '{s}' for 'rust.codegen-backends' might fail. \ + Codegen backends are mostly defined without the '{CODEGEN_BACKEND_PREFIX}' prefix. \ + In this case, it would be referred to as '{backend}'."); + } + } + + INTERNER.intern_str(s) + }).collect(); } config.rust_codegen_units = rust.codegen_units.map(threads_from_config); diff --git a/src/doc/unstable-book/src/compiler-flags/profile_sample_use.md b/src/doc/unstable-book/src/compiler-flags/profile_sample_use.md index ce894ce6ac7f..2dd1f6f8e1a3 100644 --- a/src/doc/unstable-book/src/compiler-flags/profile_sample_use.md +++ b/src/doc/unstable-book/src/compiler-flags/profile_sample_use.md @@ -1,4 +1,4 @@ -# `profile-sample-use +# `profile-sample-use` --- diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 2655d93599e7..71671273c57b 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -4890,12 +4890,14 @@ Released 2018-09-13 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else [`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond +[`ignored_unit_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub +[`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor @@ -5190,6 +5192,7 @@ Released 2018-09-13 [`redundant_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure [`redundant_closure_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_call [`redundant_closure_for_method_calls`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure_for_method_calls +[`redundant_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_comparisons [`redundant_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_else [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs index 43eaccdf5a31..fca750fafc79 100644 --- a/src/tools/clippy/clippy_dev/src/main.rs +++ b/src/tools/clippy/clippy_dev/src/main.rs @@ -41,7 +41,7 @@ fn main() { matches.get_one::("type").map(String::as_str), matches.get_flag("msrv"), ) { - Ok(_) => update_lints::update(update_lints::UpdateMode::Change), + Ok(()) => update_lints::update(update_lints::UpdateMode::Change), Err(e) => eprintln!("Unable to create lint: {e}"), } }, diff --git a/src/tools/clippy/clippy_dev/src/setup/vscode.rs b/src/tools/clippy/clippy_dev/src/setup/vscode.rs index dbcdc9b59e52..204f4af2cf1b 100644 --- a/src/tools/clippy/clippy_dev/src/setup/vscode.rs +++ b/src/tools/clippy/clippy_dev/src/setup/vscode.rs @@ -47,7 +47,7 @@ fn check_install_precondition(force_override: bool) -> bool { } } else { match fs::create_dir(vs_dir_path) { - Ok(_) => { + Ok(()) => { println!("info: created `{VSCODE_DIR}` directory for clippy"); }, Err(err) => { diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs index 15ffb00da88b..181dbcf6e9a8 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,9 +1,7 @@ -use std::borrow::Cow; - use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; @@ -16,33 +14,41 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { return; } - if_chain! { - if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind; - let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)); - if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind(); - if let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind(); - if matches!((from_mutbl, to_mutbl), - (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)); + if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind + && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr)) + && let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, .. }) = cast_from.kind() + && let ty::RawPtr(TypeAndMut { ty: to_pointee_ty, mutbl: to_mutbl }) = cast_to.kind() + && matches!((from_mutbl, to_mutbl), + (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut)) // The `U` in `pointer::cast` have to be `Sized` // as explained here: https://github.com/rust-lang/rust/issues/60602. - if to_pointee_ty.is_sized(cx.tcx, cx.param_env); - then { - let mut applicability = Applicability::MachineApplicable; - let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut applicability); - let turbofish = match &cast_to_hir_ty.kind { - TyKind::Infer => Cow::Borrowed(""), - TyKind::Ptr(mut_ty) if matches!(mut_ty.ty.kind, TyKind::Infer) => Cow::Borrowed(""), - _ => Cow::Owned(format!("::<{to_pointee_ty}>")), - }; - span_lint_and_sugg( - cx, - PTR_AS_PTR, - expr.span, - "`as` casting between raw pointers without changing its mutability", - "try `pointer::cast`, a safer alternative", - format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), - applicability, - ); - } + && to_pointee_ty.is_sized(cx.tcx, cx.param_env) + { + let mut app = Applicability::MachineApplicable; + let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app); + let turbofish = match &cast_to_hir_ty.kind { + TyKind::Infer => String::new(), + TyKind::Ptr(mut_ty) => { + if matches!(mut_ty.ty.kind, TyKind::Infer) { + String::new() + } else { + format!( + "::<{}>", + snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app) + ) + } + }, + _ => return, + }; + + span_lint_and_sugg( + cx, + PTR_AS_PTR, + expr.span, + "`as` casting between raw pointers without changing its mutability", + "try `pointer::cast`, a safer alternative", + format!("{}.cast{turbofish}()", cast_expr_sugg.maybe_par()), + app, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index d4e0d2863348..db114abfc869 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -203,6 +203,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::if_let_mutex::IF_LET_MUTEX_INFO, crate::if_not_else::IF_NOT_ELSE_INFO, crate::if_then_some_else_none::IF_THEN_SOME_ELSE_NONE_INFO, + crate::ignored_unit_patterns::IGNORED_UNIT_PATTERNS_INFO, crate::implicit_hasher::IMPLICIT_HASHER_INFO, crate::implicit_return::IMPLICIT_RETURN_INFO, crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, @@ -517,6 +518,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::FLOAT_CMP_CONST_INFO, crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, crate::operators::IDENTITY_OP_INFO, + crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, crate::operators::INTEGER_DIVISION_INFO, crate::operators::MISREFACTORED_ASSIGN_OP_INFO, @@ -525,6 +527,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::operators::NEEDLESS_BITWISE_BOOL_INFO, crate::operators::OP_REF_INFO, crate::operators::PTR_EQ_INFO, + crate::operators::REDUNDANT_COMPARISONS_INFO, crate::operators::SELF_ASSIGNMENT_INFO, crate::operators::VERBOSE_BIT_MASK_INFO, crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO, diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 9900dbdee6be..d3311792cfa8 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -1,6 +1,4 @@ -use clippy_utils::diagnostics::{ - span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, -}; +use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{is_lint_allowed, match_def_path, paths}; use if_chain::if_chain; @@ -8,15 +6,14 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::{ - self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, - UnsafeSource, Unsafety, + self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource, Unsafety, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, - ToPredicate, TraitPredicate, Ty, TyCtxt, + self, ClauseKind, GenericArgKind, GenericParamDefKind, ImplPolarity, ParamEnv, ToPredicate, TraitPredicate, Ty, + TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::def_id::LocalDefId; @@ -207,10 +204,13 @@ declare_lint_pass!(Derive => [ impl<'tcx> LateLintPass<'tcx> for Derive { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if let ItemKind::Impl(Impl { of_trait: Some(ref trait_ref), .. }) = item.kind { + if let ItemKind::Impl(Impl { + of_trait: Some(ref trait_ref), + .. + }) = item.kind + { let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); - let is_automatically_derived = - cx.tcx.has_attr(item.owner_id, sym::automatically_derived); + let is_automatically_derived = cx.tcx.has_attr(item.owner_id, sym::automatically_derived); check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived); check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived); @@ -327,12 +327,7 @@ fn check_ord_partial_ord<'tcx>( } /// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint. -fn check_copy_clone<'tcx>( - cx: &LateContext<'tcx>, - item: &Item<'_>, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { +fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { let clone_id = match cx.tcx.lang_items().clone_trait() { Some(id) if trait_ref.trait_def_id() == Some(id) => id, _ => return, @@ -350,9 +345,10 @@ fn check_copy_clone<'tcx>( if !is_copy(cx, ty) { if ty_subs.non_erasable_generics().next().is_some() { let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(©_id).map_or(false, |impls| { - impls - .iter() - .any(|&id| matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) if ty_adt.did() == adt.did())) + impls.iter().any(|&id| { + matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _) + if ty_adt.did() == adt.did()) + }) }); if !has_copy_impl { return; @@ -431,14 +427,7 @@ struct UnsafeVisitor<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { type NestedFilter = nested_filter::All; - fn visit_fn( - &mut self, - kind: FnKind<'tcx>, - decl: &'tcx FnDecl<'_>, - body_id: BodyId, - _: Span, - id: LocalDefId, - ) { + fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) { if self.has_unsafe { return; } @@ -474,12 +463,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> { } /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. -fn check_partial_eq_without_eq<'tcx>( - cx: &LateContext<'tcx>, - span: Span, - trait_ref: &hir::TraitRef<'_>, - ty: Ty<'tcx>, -) { +fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { if_chain! { if let ty::Adt(adt, args) = ty.kind(); if cx.tcx.visibility(adt.did()).is_public(); diff --git a/src/tools/clippy/clippy_lints/src/doc.rs b/src/tools/clippy/clippy_lints/src/doc.rs index 2c4d93e33ba7..e29ab634c979 100644 --- a/src/tools/clippy/clippy_lints/src/doc.rs +++ b/src/tools/clippy/clippy_lints/src/doc.rs @@ -716,10 +716,7 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, span: Span) { let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); let fallback_bundle = rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); - let emitter = EmitterWriter::new( - Box::new(io::sink()), - fallback_bundle, - ); + let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let sess = ParseSess::with_span_handler(handler, sm); diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index ba7957b0decc..38066503c079 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -10,8 +10,8 @@ use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPat use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{ - self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, - GenericArgsRef, ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults, + self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef, + ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; diff --git a/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs new file mode 100644 index 000000000000..c635120b8824 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ignored_unit_patterns.rs @@ -0,0 +1,52 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use hir::PatKind; +use rustc_errors::Applicability; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `_` in patterns of type `()`. + /// + /// ### Why is this bad? + /// Matching with `()` explicitly instead of `_` outlines + /// the fact that the pattern contains no data. Also it + /// would detect a type change that `_` would ignore. + /// + /// ### Example + /// ```rust + /// match std::fs::create_dir("tmp-work-dir") { + /// Ok(_) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), + /// } + /// ``` + /// Use instead: + /// ```rust + /// match std::fs::create_dir("tmp-work-dir") { + /// Ok(()) => println!("Working directory created"), + /// Err(s) => eprintln!("Could not create directory: {s}"), + /// } + /// ``` + #[clippy::version = "1.73.0"] + pub IGNORED_UNIT_PATTERNS, + pedantic, + "suggest replacing `_` by `()` in patterns where appropriate" +} +declare_lint_pass!(IgnoredUnitPatterns => [IGNORED_UNIT_PATTERNS]); + +impl<'tcx> LateLintPass<'tcx> for IgnoredUnitPatterns { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx hir::Pat<'tcx>) { + if matches!(pat.kind, PatKind::Wild) && cx.typeck_results().pat_ty(pat).is_unit() { + span_lint_and_sugg( + cx, + IGNORED_UNIT_PATTERNS, + pat.span, + "matching over `()` is more explicit", + "use `()` instead of `_`", + String::from("()"), + Applicability::MachineApplicable, + ); + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 9d6096ccb2ae..358004cf460b 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -147,6 +147,7 @@ mod future_not_send; mod if_let_mutex; mod if_not_else; mod if_then_some_else_none; +mod ignored_unit_patterns; mod implicit_hasher; mod implicit_return; mod implicit_saturating_add; @@ -1093,6 +1094,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: }) }); store.register_late_pass(|_| Box::new(redundant_locals::RedundantLocals)); + store.register_late_pass(|_| Box::new(ignored_unit_patterns::IgnoredUnitPatterns)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs index dd77c69ef88a..cc19ac55e5e4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -162,7 +162,9 @@ fn never_loop_expr<'tcx>( ExprKind::Binary(_, e1, e2) | ExprKind::Assign(e1, e2, _) | ExprKind::AssignOp(_, e1, e2) - | ExprKind::Index(e1, e2, _) => never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id), + | ExprKind::Index(e1, e2, _) => { + never_loop_expr_all(cx, &mut [e1, e2].iter().copied(), ignore_ids, main_loop_id) + }, ExprKind::Loop(b, _, _, _) => { // Break can come from the inner loop so remove them. absorb_break(never_loop_block(cx, b, ignore_ids, main_loop_id)) diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index f48a5d9d2456..88db7ae6aece 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if !in_external_macro(cx.sess(), expr.span) && ( - matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) + matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst) || cx.tcx.features().active(sym!(const_float_classify)) ) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs index 6383326aa38d..29af4812351e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::path_to_local; use clippy_utils::source::snippet_with_applicability; use clippy_utils::visitors::{for_each_expr, is_local_used}; +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind}; @@ -160,6 +161,11 @@ fn emit_redundant_guards<'tcx>( } /// Checks if the given `Expr` can also be represented as a `Pat`. +/// +/// All literals generally also work as patterns, however float literals are special. +/// They are currently (as of 2023/08/08) still allowed in patterns, but that will become +/// an error in the future, and rustc already actively warns against this (see rust#41620), +/// so we don't consider those as usable within patterns for linting purposes. fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { for_each_expr(expr, |expr| { if match expr.kind { @@ -177,8 +183,8 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { ExprKind::AddrOf(..) | ExprKind::Array(..) | ExprKind::Tup(..) - | ExprKind::Struct(..) - | ExprKind::Lit(..) => true, + | ExprKind::Struct(..) => true, + ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true, _ => false, } { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/expect_used.rs deleted file mode 100644 index 614610335a13..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/expect_used.rs +++ /dev/null @@ -1,44 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_cfg_test, is_in_test_function}; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::EXPECT_USED; - -/// lint use of `expect()` or `expect_err` for `Result` and `expect()` for `Option`. -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - is_err: bool, - allow_expect_in_tests: bool, -) { - let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((EXPECT_USED, "an `Option`", "None", "")) - } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((EXPECT_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) - } else { - None - }; - - let method = if is_err { "expect_err" } else { "expect" }; - - if allow_expect_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { - return; - } - - if let Some((lint, kind, none_value, none_prefix)) = mess { - span_lint_and_help( - cx, - lint, - expr.span, - &format!("used `{method}()` on {kind} value"), - None, - &format!("if this value is {none_prefix}`{none_value}`, it will panic"), - ); - } -} diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 4aee22a4afc3..fafc97097700 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,3 +1,4 @@ +use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::BOOL_THEN; use clippy_utils::source::snippet_opt; @@ -7,10 +8,9 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Binder; use rustc_span::{sym, Span}; -use super::FILTER_MAP_BOOL_THEN; - pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &Expr<'_>, call_span: Span) { if !in_external_macro(cx.sess(), expr.span) && is_trait_method(cx, expr, sym::Iterator) @@ -21,7 +21,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & // `inputs` and `params` here as we need both the type and the span && let param_ty = closure.fn_decl.inputs[0] && let param = body.params[0] - && is_copy(cx, cx.typeck_results().node_type(param_ty.hir_id).peel_refs()) + // Issue #11309 + && let param_ty = cx.tcx.liberate_late_bound_regions( + closure.def_id.to_def_id(), + Binder::bind_with_vars( + cx.typeck_results().node_type(param_ty.hir_id), + cx.tcx.late_bound_vars(cx.tcx.hir().local_def_id_to_hir_id(closure.def_id)), + ), + ) + && is_copy(cx, param_ty) && let ExprKind::MethodCall(_, recv, [then_arg], _) = value.kind && let ExprKind::Closure(then_closure) = then_arg.kind && let then_body = peel_blocks(cx.tcx.hir().body(then_closure.body).value) diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index dd694ce7393e..42756b27d014 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -17,7 +17,6 @@ mod collapsible_str_replace; mod drain_collect; mod err_expect; mod expect_fun_call; -mod expect_used; mod extend_with_drain; mod filetype_is_file; mod filter_map; @@ -105,7 +104,7 @@ mod unnecessary_lazy_eval; mod unnecessary_literal_unwrap; mod unnecessary_sort_by; mod unnecessary_to_owned; -mod unwrap_used; +mod unwrap_expect_used; mod useless_asref; mod utils; mod vec_resize_to_zero; @@ -3881,6 +3880,13 @@ impl Methods { unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); } }, + ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind + && let body = cx.tcx.hir().body(arg.body) + && let [param] = body.params + && let Some(("chars", recv, _, _, _)) = method_call(recv) => + { + string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); + } ("arg", [arg]) => { suspicious_command_arg_space::check(cx, recv, arg, span); } @@ -3941,13 +3947,27 @@ impl Methods { match method_call(recv) { Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv), - _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), + _ => unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ), } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, ("expect_err", [_]) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests); + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_expect_in_tests, + unwrap_expect_used::Variant::Expect, + ); }, ("extend", [arg]) => { string_extend_chars::check(cx, expr, recv, arg); @@ -3999,20 +4019,9 @@ impl Methods { unnecessary_join::check(cx, expr, recv, join_arg, span); } }, - ("skip", [arg]) => { - iter_skip_zero::check(cx, expr, arg); - - if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); - } - } - } ("last", []) => { - if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); - } + if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } }, ("lock", []) => { @@ -4060,13 +4069,6 @@ impl Methods { } } }, - ("any", [arg]) if let ExprKind::Closure(arg) = arg.kind - && let body = cx.tcx.hir().body(arg.body) - && let [param] = body.params - && let Some(("chars", recv, _, _, _)) = method_call(recv) => - { - string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv); - } ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", recv2, [], _, _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("cloned", recv2, [], _, _)) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), @@ -4120,6 +4122,13 @@ impl Methods { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, + ("skip", [arg]) => { + iter_skip_zero::check(cx, expr, arg); + + if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); + } + } ("sort", []) => { stable_sort_primitive::check(cx, expr, recv); }, @@ -4142,10 +4151,8 @@ impl Methods { }, ("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg), ("take", [_arg]) => { - if let Some((name2, recv2, args2, _span2, _)) = method_call(recv) { - if let ("cloned", []) = (name2, args2) { - iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); - } + if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) { + iter_overeager_cloned::check(cx, expr, recv, recv2, false, false); } }, ("take", []) => needless_option_take::check(cx, expr, recv), @@ -4180,11 +4187,25 @@ impl Methods { _ => {}, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_used::check(cx, expr, recv, false, self.allow_unwrap_in_tests); + unwrap_expect_used::check( + cx, + expr, + recv, + false, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); }, ("unwrap_err", []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); - unwrap_used::check(cx, expr, recv, true, self.allow_unwrap_in_tests); + unwrap_expect_used::check( + cx, + expr, + recv, + true, + self.allow_unwrap_in_tests, + unwrap_expect_used::Variant::Unwrap, + ); }, ("unwrap_or", [u_arg]) => { match method_call(recv) { @@ -4214,6 +4235,9 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, + ("write", []) => { + readonly_write_lock::check(cx, expr, recv); + } ("zip", [arg]) => { if let ExprKind::MethodCall(name, iter_recv, [], _) = recv.kind && name.ident.name == sym::iter @@ -4221,9 +4245,6 @@ impl Methods { range_zip_with_len::check(cx, expr, iter_recv, arg); } }, - ("write", []) => { - readonly_write_lock::check(cx, expr, recv); - } _ => {}, } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs new file mode 100644 index 000000000000..7bd16b473ce4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs @@ -0,0 +1,83 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::ty::{is_never_like, is_type_diagnostic_item}; +use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed}; +use rustc_hir::Expr; +use rustc_lint::{LateContext, Lint}; +use rustc_middle::ty; +use rustc_span::sym; + +use super::{EXPECT_USED, UNWRAP_USED}; + +#[derive(Clone, Copy, Eq, PartialEq)] +pub(super) enum Variant { + Unwrap, + Expect, +} + +impl Variant { + fn method_name(self, is_err: bool) -> &'static str { + match (self, is_err) { + (Variant::Unwrap, true) => "unwrap_err", + (Variant::Unwrap, false) => "unwrap", + (Variant::Expect, true) => "expect_err", + (Variant::Expect, false) => "expect", + } + } + + fn lint(self) -> &'static Lint { + match self { + Variant::Unwrap => UNWRAP_USED, + Variant::Expect => EXPECT_USED, + } + } +} + +/// Lint usage of `unwrap` or `unwrap_err` for `Result` and `unwrap()` for `Option` (and their +/// `expect` counterparts). +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + is_err: bool, + allow_unwrap_in_tests: bool, + variant: Variant, +) { + let ty = cx.typeck_results().expr_ty(recv).peel_refs(); + + let (kind, none_value, none_prefix) = if is_type_diagnostic_item(cx, ty, sym::Option) && !is_err { + ("an `Option`", "None", "") + } else if is_type_diagnostic_item(cx, ty, sym::Result) + && let ty::Adt(_, substs) = ty.kind() + && let Some(t_or_e_ty) = substs[usize::from(!is_err)].as_type() + { + if is_never_like(t_or_e_ty) { + return; + } + + ("a `Result`", if is_err { "Ok" } else { "Err" }, "an ") + } else { + return; + }; + + let method_suffix = if is_err { "_err" } else { "" }; + + if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { + return; + } + + span_lint_and_then( + cx, + variant.lint(), + expr.span, + &format!("used `{}()` on {kind} value", variant.method_name(is_err)), + |diag| { + diag.note(format!("if this value is {none_prefix}`{none_value}`, it will panic")); + + if variant == Variant::Unwrap && is_lint_allowed(cx, EXPECT_USED, expr.hir_id) { + diag.help(format!( + "consider using `expect{method_suffix}()` to provide a better panic message" + )); + } + }, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs deleted file mode 100644 index 5e4c3daee644..000000000000 --- a/src/tools/clippy/clippy_lints/src/methods/unwrap_used.rs +++ /dev/null @@ -1,53 +0,0 @@ -use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed}; -use rustc_hir as hir; -use rustc_lint::LateContext; -use rustc_span::sym; - -use super::{EXPECT_USED, UNWRAP_USED}; - -/// lint use of `unwrap()` or `unwrap_err` for `Result` and `unwrap()` for `Option`. -pub(super) fn check( - cx: &LateContext<'_>, - expr: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - is_err: bool, - allow_unwrap_in_tests: bool, -) { - let obj_ty = cx.typeck_results().expr_ty(recv).peel_refs(); - - let mess = if is_type_diagnostic_item(cx, obj_ty, sym::Option) && !is_err { - Some((UNWRAP_USED, "an `Option`", "None", "")) - } else if is_type_diagnostic_item(cx, obj_ty, sym::Result) { - Some((UNWRAP_USED, "a `Result`", if is_err { "Ok" } else { "Err" }, "an ")) - } else { - None - }; - - let method_suffix = if is_err { "_err" } else { "" }; - - if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) { - return; - } - - if let Some((lint, kind, none_value, none_prefix)) = mess { - let help = if is_lint_allowed(cx, EXPECT_USED, expr.hir_id) { - format!( - "if you don't want to handle the `{none_value}` case gracefully, consider \ - using `expect{method_suffix}()` to provide a better panic message" - ) - } else { - format!("if this value is {none_prefix}`{none_value}`, it will panic") - }; - - span_lint_and_help( - cx, - lint, - expr.span, - &format!("used `unwrap{method_suffix}()` on {kind} value"), - None, - &help, - ); - } -} diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index a41d5a9ce8d2..93f6025c71d6 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -74,7 +74,6 @@ fn is_executable_or_proc_macro(cx: &LateContext<'_>) -> bool { use rustc_session::config::CrateType; cx.tcx - .sess .crate_types() .iter() .any(|t: &CrateType| matches!(t, CrateType::Executable | CrateType::ProcMacro)) diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs index 4b20aecad4a1..e53e146ec5db 100644 --- a/src/tools/clippy/clippy_lints/src/mut_reference.rs +++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs @@ -37,6 +37,11 @@ declare_lint_pass!(UnnecessaryMutPassed => [UNNECESSARY_MUT_PASSED]); impl<'tcx> LateLintPass<'tcx> for UnnecessaryMutPassed { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { + if e.span.from_expansion() { + // Issue #11268 + return; + } + match e.kind { ExprKind::Call(fn_expr, arguments) => { if let ExprKind::Path(ref path) = fn_expr.kind { diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs new file mode 100644 index 000000000000..abe8df195434 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -0,0 +1,207 @@ +#![allow(clippy::match_same_arms)] + +use std::cmp::Ordering; + +use clippy_utils::consts::{constant, Constant}; +use if_chain::if_chain; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_middle::ty::layout::HasTyCtxt; +use rustc_middle::ty::{Ty, TypeckResults}; +use rustc_span::source_map::{Span, Spanned}; + +use clippy_utils::diagnostics::span_lint_and_note; +use clippy_utils::source::snippet; +use clippy_utils::SpanlessEq; + +use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS}; + +// Extract a comparison between a const and non-const +// Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42` +fn comparison_to_const<'tcx>( + cx: &LateContext<'tcx>, + typeck: &TypeckResults<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> { + if_chain! { + if let ExprKind::Binary(operator, left, right) = expr.kind; + if let Ok(cmp_op) = CmpOp::try_from(operator.node); + then { + match (constant(cx, typeck, left), constant(cx, typeck, right)) { + (Some(_), Some(_)) => None, + (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))), + (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))), + _ => None, + } + } else { + None + } + } +} + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + and_op: Spanned, + left_cond: &'tcx Expr<'tcx>, + right_cond: &'tcx Expr<'tcx>, + span: Span, +) { + if_chain! { + // Ensure that the binary operator is && + if and_op.node == BinOpKind::And; + + // Check that both operands to '&&' are themselves a binary operation + // The `comparison_to_const` step also checks this, so this step is just an optimization + if let ExprKind::Binary(_, _, _) = left_cond.kind; + if let ExprKind::Binary(_, _, _) = right_cond.kind; + + let typeck = cx.typeck_results(); + + // Check that both operands to '&&' compare a non-literal to a literal + if let Some((left_cmp_op, left_expr, left_const_expr, left_const, left_type)) = + comparison_to_const(cx, typeck, left_cond); + if let Some((right_cmp_op, right_expr, right_const_expr, right_const, right_type)) = + comparison_to_const(cx, typeck, right_cond); + + if left_type == right_type; + + // Check that the same expression is compared in both comparisons + if SpanlessEq::new(cx).eq_expr(left_expr, right_expr); + + if !left_expr.can_have_side_effects(); + + // Compare the two constant expressions + if let Some(ordering) = Constant::partial_cmp(cx.tcx(), left_type, &left_const, &right_const); + + // Rule out the `x >= 42 && x <= 42` corner case immediately + // Mostly to simplify the implementation, but it is also covered by `clippy::double_comparisons` + if !matches!( + (&left_cmp_op, &right_cmp_op, ordering), + (CmpOp::Le | CmpOp::Ge, CmpOp::Le | CmpOp::Ge, Ordering::Equal) + ); + + then { + if left_cmp_op.direction() == right_cmp_op.direction() { + let lhs_str = snippet(cx, left_cond.span, ""); + let rhs_str = snippet(cx, right_cond.span, ""); + // We already know that either side of `&&` has no effect, + // but emit a different error message depending on which side it is + if left_side_is_useless(left_cmp_op, ordering) { + span_lint_and_note( + cx, + REDUNDANT_COMPARISONS, + span, + "left-hand side of `&&` operator has no effect", + Some(left_cond.span.until(right_cond.span)), + &format!("`if `{rhs_str}` evaluates to true, {lhs_str}` will always evaluate to true as well"), + ); + } else { + span_lint_and_note( + cx, + REDUNDANT_COMPARISONS, + span, + "right-hand side of `&&` operator has no effect", + Some(and_op.span.to(right_cond.span)), + &format!("`if `{lhs_str}` evaluates to true, {rhs_str}` will always evaluate to true as well"), + ); + } + // We could autofix this error but choose not to, + // because code triggering this lint probably not behaving correctly in the first place + } + else if !comparison_is_possible(left_cmp_op.direction(), ordering) { + let expr_str = snippet(cx, left_expr.span, ".."); + let lhs_str = snippet(cx, left_const_expr.span, ""); + let rhs_str = snippet(cx, right_const_expr.span, ""); + let note = match ordering { + Ordering::Less => format!("since `{lhs_str}` < `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), + Ordering::Equal => format!("`{expr_str}` cannot simultaneously be greater than and less than `{lhs_str}`"), + Ordering::Greater => format!("since `{lhs_str}` > `{rhs_str}`, the expression evaluates to false for any value of `{expr_str}`"), + }; + span_lint_and_note( + cx, + IMPOSSIBLE_COMPARISONS, + span, + "boolean expression will never evaluate to 'true'", + None, + ¬e, + ); + }; + } + } +} + +fn left_side_is_useless(left_cmp_op: CmpOp, ordering: Ordering) -> bool { + // Special-case for equal constants with an inclusive comparison + if ordering == Ordering::Equal { + match left_cmp_op { + CmpOp::Lt | CmpOp::Gt => false, + CmpOp::Le | CmpOp::Ge => true, + } + } else { + match (left_cmp_op.direction(), ordering) { + (CmpOpDirection::Lesser, Ordering::Less) => false, + (CmpOpDirection::Lesser, Ordering::Equal) => false, + (CmpOpDirection::Lesser, Ordering::Greater) => true, + (CmpOpDirection::Greater, Ordering::Less) => true, + (CmpOpDirection::Greater, Ordering::Equal) => false, + (CmpOpDirection::Greater, Ordering::Greater) => false, + } + } +} + +fn comparison_is_possible(left_cmp_direction: CmpOpDirection, ordering: Ordering) -> bool { + match (left_cmp_direction, ordering) { + (CmpOpDirection::Lesser, Ordering::Less | Ordering::Equal) => false, + (CmpOpDirection::Lesser, Ordering::Greater) => true, + (CmpOpDirection::Greater, Ordering::Greater | Ordering::Equal) => false, + (CmpOpDirection::Greater, Ordering::Less) => true, + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +enum CmpOpDirection { + Lesser, + Greater, +} + +#[derive(Clone, Copy)] +enum CmpOp { + Lt, + Le, + Ge, + Gt, +} + +impl CmpOp { + fn reverse(self) -> Self { + match self { + CmpOp::Lt => CmpOp::Gt, + CmpOp::Le => CmpOp::Ge, + CmpOp::Ge => CmpOp::Le, + CmpOp::Gt => CmpOp::Lt, + } + } + + fn direction(self) -> CmpOpDirection { + match self { + CmpOp::Lt => CmpOpDirection::Lesser, + CmpOp::Le => CmpOpDirection::Lesser, + CmpOp::Ge => CmpOpDirection::Greater, + CmpOp::Gt => CmpOpDirection::Greater, + } + } +} + +impl TryFrom for CmpOp { + type Error = (); + + fn try_from(bin_op: BinOpKind) -> Result { + match bin_op { + BinOpKind::Lt => Ok(CmpOp::Lt), + BinOpKind::Le => Ok(CmpOp::Le), + BinOpKind::Ge => Ok(CmpOp::Ge), + BinOpKind::Gt => Ok(CmpOp::Gt), + _ => Err(()), + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index 2cf15adda01a..4635e1164cd3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -2,6 +2,7 @@ mod absurd_extreme_comparisons; mod assign_op_pattern; mod bit_mask; mod cmp_owned; +mod const_comparisons; mod double_comparison; mod duration_subsec; mod eq_op; @@ -298,6 +299,45 @@ declare_clippy_lint! { "unnecessary double comparisons that can be simplified" } +declare_clippy_lint! { + /// ### What it does + /// Checks for double comparisons that can never succeed + /// + /// ### Why is this bad? + /// The whole expression can be replaced by `false`, + /// which is probably not the programmer's intention + /// + /// ### Example + /// ```rust + /// # let status_code = 200; + /// if status_code <= 400 && status_code > 500 {} + /// ``` + #[clippy::version = "1.71.0"] + pub IMPOSSIBLE_COMPARISONS, + correctness, + "double comparisons that will never evaluate to `true`" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for ineffective double comparisons against constants. + /// + /// ### Why is this bad? + /// Only one of the comparisons has any effect on the result, the programmer + /// probably intended to flip one of the comparison operators, or compare a + /// different value entirely. + /// + /// ### Example + /// ```rust + /// # let status_code = 200; + /// if status_code <= 400 && status_code < 500 {} + /// ``` + #[clippy::version = "1.71.0"] + pub REDUNDANT_COMPARISONS, + correctness, + "double comparisons where one of them can be removed" +} + declare_clippy_lint! { /// ### What it does /// Checks for calculation of subsecond microseconds or milliseconds @@ -742,6 +782,8 @@ impl_lint_pass!(Operators => [ INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK, DOUBLE_COMPARISONS, + IMPOSSIBLE_COMPARISONS, + REDUNDANT_COMPARISONS, DURATION_SUBSEC, EQ_OP, OP_REF, @@ -786,6 +828,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { bit_mask::check(cx, e, op.node, lhs, rhs); verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold); double_comparison::check(cx, op.node, lhs, rhs, e.span); + const_comparisons::check(cx, op, lhs, rhs, e.span); duration_subsec::check(cx, e, op.node, lhs, rhs); float_equality_without_abs::check(cx, e, op.node, lhs, rhs); integer_division::check(cx, e, op.node, lhs, rhs); diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs index 40da002f4ff0..a7a7f4fd8fa2 100644 --- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs @@ -155,7 +155,7 @@ fn try_get_option_occurrence<'tcx>( }); if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind { match some_captures.get(local_id) - .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id))) + .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|()| none_captures.get(local_id))) { Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None, Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index f5502cffb669..734ca2914f5d 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -1,11 +1,13 @@ use crate::manual_let_else::{MatchLintBehaviour, MANUAL_LET_ELSE}; +use crate::question_mark_used::QUESTION_MARK_USED; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{ - eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_path_lang_item, is_res_lang_ctor, - pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt, + eq_expr_value, get_parent_node, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item, + is_res_lang_ctor, pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, + peel_blocks_with_stmt, }; use if_chain::if_chain; use rustc_errors::Applicability; @@ -299,13 +301,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &rustc_hir::Block<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) { + return; + } + if !in_constant(cx, stmt.hir_id) { check_let_some_else_return_none(cx, stmt); } self.check_manual_let_else(cx, stmt); } fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !in_constant(cx, expr.hir_id) { + if !in_constant(cx, expr.hir_id) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) { self.check_is_none_or_err_and_early_return(cx, expr); self.check_if_let_some_or_err_and_early_return(cx, expr); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index 896bd79b20bf..0c89c7ee47d3 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::needs_ordered_drop; +use rustc_ast::Mutability; use rustc_hir::def::Res; use rustc_hir::{ BindingAnnotation, ByRef, Expr, ExprKind, HirId, Local, Node, Pat, PatKind, QPath, @@ -9,6 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::Ident; +use rustc_span::DesugaringKind; declare_clippy_lint! { /// ### What it does @@ -47,6 +49,7 @@ declare_lint_pass!(RedundantLocals => [REDUNDANT_LOCALS]); impl<'tcx> LateLintPass<'tcx> for RedundantLocals { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { if_chain! { + if !local.span.is_desugaring(DesugaringKind::Async); // the pattern is a single by-value binding if let PatKind::Binding(BindingAnnotation(ByRef::No, mutability), _, ident, None) = local.pat.kind; // the binding is not type-ascribed @@ -62,6 +65,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals { if let Node::Pat(binding_pat) = cx.tcx.hir().get(binding_id); // the previous binding has the same mutability if find_binding(binding_pat, ident).unwrap().1 == mutability; + // the local does not change the effect of assignments to the binding. see #11290 + if !affects_assignments(cx, mutability, binding_id, local.hir_id); // the local does not affect the code's drop behavior if !affects_drop_behavior(cx, binding_id, local.hir_id, expr); // the local is user-controlled @@ -97,6 +102,14 @@ fn find_binding(pat: &Pat<'_>, name: Ident) -> Option { ret } +/// Check if a rebinding of a local changes the effect of assignments to the binding. +fn affects_assignments(cx: &LateContext<'_>, mutability: Mutability, bind: HirId, rebind: HirId) -> bool { + let hir = cx.tcx.hir(); + + // the binding is mutable and the rebinding is in a different scope than the original binding + mutability == Mutability::Mut && hir.get_enclosing_scope(bind) != hir.get_enclosing_scope(rebind) +} + /// Check if a rebinding of a local affects the code's drop behavior. fn affects_drop_behavior<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs index 8e9234bba3c8..3e963d798928 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs @@ -1,6 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_lint_allowed; use rustc_ast::LitKind; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -45,8 +47,8 @@ fn is_same_type<'tcx>(cx: &LateContext<'tcx>, ty_resolved_path: hir::def::Res, f return primty.name() == func_return_type_sym; } - // type annotation is any other non generic type - if let hir::def::Res::Def(_, defid) = ty_resolved_path + // type annotation is a non generic type + if let hir::def::Res::Def(DefKind::Struct | DefKind::Union | DefKind::Enum, defid) = ty_resolved_path && let Some(annotation_ty) = cx.tcx.type_of(defid).no_bound_vars() { return annotation_ty == func_return_type; @@ -130,8 +132,9 @@ fn extract_primty(ty_kind: &hir::TyKind<'_>) -> Option { impl LateLintPass<'_> for RedundantTypeAnnotations { fn check_local<'tcx>(&mut self, cx: &LateContext<'tcx>, local: &'tcx rustc_hir::Local<'tcx>) { - // type annotation part - if !local.span.from_expansion() + if !is_lint_allowed(cx, REDUNDANT_TYPE_ANNOTATIONS, local.hir_id) + // type annotation part + && !local.span.from_expansion() && let Some(ty) = &local.ty // initialization part diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs index 54a33eb2986d..c9ab622ad25d 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -20,18 +20,27 @@ declare_clippy_lint! { /// These structures are non-idiomatic and less efficient than simply using /// `vec![0; len]`. /// + /// Specifically, for `vec![0; len]`, the compiler can use a specialized type of allocation + /// that also zero-initializes the allocated memory in the same call + /// (see: [alloc_zeroed](https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html#method.alloc_zeroed)). + /// + /// Writing `Vec::new()` followed by `vec.resize(len, 0)` is suboptimal because, + /// while it does do the same number of allocations, + /// it involves two operations for allocating and initializing. + /// The `resize` call first allocates memory (since `Vec::new()` did not), and only *then* zero-initializes it. + /// /// ### Example /// ```rust /// # use core::iter::repeat; /// # let len = 4; - /// let mut vec1 = Vec::with_capacity(len); + /// let mut vec1 = Vec::new(); /// vec1.resize(len, 0); /// - /// let mut vec1 = Vec::with_capacity(len); - /// vec1.resize(vec1.capacity(), 0); - /// /// let mut vec2 = Vec::with_capacity(len); - /// vec2.extend(repeat(0).take(len)); + /// vec2.resize(len, 0); + /// + /// let mut vec3 = Vec::with_capacity(len); + /// vec3.extend(repeat(0).take(len)); /// ``` /// /// Use instead: @@ -39,6 +48,7 @@ declare_clippy_lint! { /// # let len = 4; /// let mut vec1 = vec![0; len]; /// let mut vec2 = vec![0; len]; + /// let mut vec3 = vec![0; len]; /// ``` #[clippy::version = "1.32.0"] pub SLOW_VECTOR_INITIALIZATION, diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs index 6fa49afe0ecd..8e156b8829b9 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs @@ -1,5 +1,7 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::snippet_with_context; +use clippy_utils::source::snippet; +use rustc_ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -28,27 +30,29 @@ declare_lint_pass!(ConfusingXorAndPow => [SUSPICIOUS_XOR_USED_AS_POW]); impl LateLintPass<'_> for ConfusingXorAndPow { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { - if !in_external_macro(cx.sess(), expr.span) && - let ExprKind::Binary(op, left, right) = &expr.kind && - op.node == BinOpKind::BitXor && - left.span.ctxt() == right.span.ctxt() && - let ExprKind::Lit(lit_left) = &left.kind && - let ExprKind::Lit(lit_right) = &right.kind && - let snip_left = snippet_with_context(cx, lit_left.span, lit_left.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && - let snip_right = snippet_with_context(cx, lit_right.span, lit_right.span.ctxt(), "..", &mut Applicability::MaybeIncorrect) && - let Some(left_val) = NumericLiteral::from_lit_kind(&snip_left.0, &lit_left.node) && - let Some(right_val) = NumericLiteral::from_lit_kind(&snip_right.0, &lit_right.node) && - left_val.is_decimal() && - right_val.is_decimal() { - clippy_utils::diagnostics::span_lint_and_sugg( - cx, - SUSPICIOUS_XOR_USED_AS_POW, - expr.span, - "`^` is not the exponentiation operator", - "did you mean to write", - format!("{}.pow({})", left_val.format(), right_val.format()), - Applicability::MaybeIncorrect, - ); + if !in_external_macro(cx.sess(), expr.span) + && let ExprKind::Binary(op, left, right) = &expr.kind + && op.node == BinOpKind::BitXor + && left.span.ctxt() == right.span.ctxt() + && let ExprKind::Lit(lit_left) = &left.kind + && let ExprKind::Lit(lit_right) = &right.kind + && matches!(lit_right.node, LitKind::Int(..) | LitKind::Float(..)) + && matches!(lit_left.node, LitKind::Int(..) | LitKind::Float(..)) + && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node).is_some_and(|x| x.is_decimal()) + { + span_lint_and_sugg( + cx, + SUSPICIOUS_XOR_USED_AS_POW, + expr.span, + "`^` is not the exponentiation operator", + "did you mean to write", + format!( + "{}.pow({})", + lit_left.node, + lit_right.node + ), + Applicability::MaybeIncorrect, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs index e4906944c8d0..4ed985f54d0e 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs @@ -34,7 +34,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths { let mod_name = &cx.tcx.item_name(local_def_id.to_def_id()); if_chain! { if mod_name.as_str() == "paths"; - if let hir::ItemKind::Const(ty, body_id) = item.kind; + if let hir::ItemKind::Const(ty, _, body_id) = item.kind; let ty = hir_ty_to_ty(cx.tcx, ty); if let ty::Array(el_ty, _) = &ty.kind(); if let ty::Ref(_, el_ty, _) = &el_ty.kind(); diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index 2d0d6f559ad6..140cfa2194fc 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -178,7 +178,9 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { (Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r), (Break(ll, le), Break(rl, re)) => eq_label(ll, rl) && eq_expr_opt(le, re), (Continue(ll), Continue(rl)) => eq_label(ll, rl), - (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => eq_expr(l1, r1) && eq_expr(l2, r2), + (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => { + eq_expr(l1, r1) && eq_expr(l2, r2) + }, (AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv), (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp), (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm), diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index d38e3f1ae76b..adeb673b6b9d 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -329,7 +329,7 @@ pub struct ConstEvalLateContext<'a, 'tcx> { } impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> { - fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { + pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self { Self { lcx, typeck_results, diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index f0a777c5b895..4c695cb9b6e1 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -14,7 +14,7 @@ use rustc_middle::mir::{ Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; -use rustc_middle::traits::{ImplSource, ObligationCause, BuiltinImplSource}; +use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt}; use rustc_semver::RustcVersion; @@ -425,5 +425,5 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx> ocx.select_all_or_error().is_empty() } - !ty.needs_drop(tcx, ConstCx::new(tcx, body).param_env) + !ty.needs_drop(tcx, ConstCx::new(tcx, body).param_env) } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index a43a81bc63a1..ee5a49a20733 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -1010,7 +1010,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> { projections_handled = true; }, // note: unable to trigger `Subslice` kind in tests - ProjectionKind::Subslice => (), + ProjectionKind::Subslice | // Doesn't have surface syntax. Only occurs in patterns. ProjectionKind::OpaqueCast => (), ProjectionKind::Deref => { diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 717664805356..a05f682aa8cd 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -1093,6 +1093,11 @@ fn assert_generic_args_match<'tcx>(tcx: TyCtxt<'tcx>, did: DefId, args: &[Generi } } +/// Returns whether `ty` is never-like; i.e., `!` (never) or an enum with zero variants. +pub fn is_never_like(ty: Ty<'_>) -> bool { + ty.is_never() || (ty.is_enum() && ty.ty_adt_def().is_some_and(|def| def.variants().is_empty())) +} + /// Makes the projection type for the named associated type in the given impl or trait impl. /// /// This function is for associated types which are "known" to exist, and as such, will only return diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 833608faff06..8b3f819f0cde 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,3 +1,3 @@ [toolchain] -channel = "nightly-2023-07-28" +channel = "nightly-2023-08-10" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] diff --git a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr index 31df0ebd9fd6..d8b158816c9f 100644 --- a/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr +++ b/src/tools/clippy/tests/ui-internal/custom_ice_message.stderr @@ -1,4 +1,5 @@ -thread '' panicked at 'Would you like some help with that?', clippy_lints/src/utils/internal_lints/produce_ice.rs +thread '' panicked at clippy_lints/src/utils/internal_lints/produce_ice.rs: +Would you like some help with that? note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace error: the compiler unexpectedly panicked. this is a bug. diff --git a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr index 9eef0e1bfaa1..815d009350f3 100644 --- a/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr +++ b/src/tools/clippy/tests/ui-toml/expect_used/expect_used.stderr @@ -4,7 +4,7 @@ error: used `expect()` on an `Option` value LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ | - = help: if this value is `None`, it will panic + = note: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` error: used `expect()` on a `Result` value @@ -13,7 +13,7 @@ error: used `expect()` on a `Result` value LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ | - = help: if this value is an `Err`, it will panic + = note: if this value is an `Err`, it will panic error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr index 4c9bdfa9dba7..10219beaf971 100644 --- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr +++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr @@ -12,7 +12,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise @@ -27,7 +28,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:40:17 @@ -41,7 +43,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:41:17 @@ -55,7 +58,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:42:17 @@ -69,7 +73,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:43:17 @@ -83,7 +88,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:47:21 @@ -97,7 +103,8 @@ error: used `unwrap()` on an `Option` value LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:52:9 @@ -111,7 +118,8 @@ error: used `unwrap()` on an `Option` value LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:53:9 @@ -125,7 +133,8 @@ error: used `unwrap()` on an `Option` value LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:54:9 @@ -139,7 +148,8 @@ error: used `unwrap()` on an `Option` value LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:55:9 @@ -153,7 +163,8 @@ error: used `unwrap()` on an `Option` value LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:67:17 @@ -167,7 +178,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:68:17 @@ -181,7 +193,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/unwrap_used.rs:75:13 diff --git a/src/tools/clippy/tests/ui/const_comparisons.rs b/src/tools/clippy/tests/ui/const_comparisons.rs new file mode 100644 index 000000000000..8e265c9141c1 --- /dev/null +++ b/src/tools/clippy/tests/ui/const_comparisons.rs @@ -0,0 +1,93 @@ +#![allow(unused)] +#![warn(clippy::impossible_comparisons)] +#![warn(clippy::redundant_comparisons)] +#![allow(clippy::no_effect)] +#![allow(clippy::short_circuit_statement)] +#![allow(clippy::manual_range_contains)] + +const STATUS_BAD_REQUEST: u16 = 400; +const STATUS_SERVER_ERROR: u16 = 500; + +struct Status { + code: u16, +} + +impl PartialEq for Status { + fn eq(&self, other: &u16) -> bool { + self.code == *other + } +} + +impl PartialOrd for Status { + fn partial_cmp(&self, other: &u16) -> Option { + self.code.partial_cmp(other) + } +} + +impl PartialEq for u16 { + fn eq(&self, other: &Status) -> bool { + *self == other.code + } +} + +impl PartialOrd for u16 { + fn partial_cmp(&self, other: &Status) -> Option { + self.partial_cmp(&other.code) + } +} + +fn main() { + let status_code = 500; // Value doesn't matter for the lint + let status = Status { code: status_code }; + + status_code >= 400 && status_code < 500; // Correct + status_code <= 400 && status_code > 500; + status_code > 500 && status_code < 400; + status_code < 500 && status_code > 500; + + // More complex expressions + status_code < { 400 } && status_code > { 500 }; + status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; + status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; + status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; + + // Comparing two different types, via the `impl PartialOrd for Status` + status < { 400 } && status > { 500 }; + status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; + status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; + status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; + + // Yoda conditions + 500 <= status_code && 600 > status_code; // Correct + 500 <= status_code && status_code <= 600; // Correct + 500 >= status_code && 600 < status_code; // Incorrect + 500 >= status_code && status_code > 600; // Incorrect + + // Yoda conditions, comparing two different types + 500 <= status && 600 > status; // Correct + 500 <= status && status <= 600; // Correct + 500 >= status && 600 < status; // Incorrect + 500 >= status && status > 600; // Incorrect + + // Expressions where one of the sides has no effect + status_code < 200 && status_code <= 299; + status_code > 200 && status_code >= 299; + + status_code >= 500 && status_code > 500; // Useless left + status_code > 500 && status_code >= 500; // Useless right + status_code <= 500 && status_code < 500; // Useless left + status_code < 500 && status_code <= 500; // Useless right + + // Other types + let name = "Steve"; + name < "Jennifer" && name > "Shannon"; + + let numbers = [1, 2]; + numbers < [3, 4] && numbers > [5, 6]; + + let letter = 'a'; + letter < 'b' && letter > 'c'; + + let area = 42.0; + area < std::f32::consts::E && area > std::f32::consts::PI; +} diff --git a/src/tools/clippy/tests/ui/const_comparisons.stderr b/src/tools/clippy/tests/ui/const_comparisons.stderr new file mode 100644 index 000000000000..90e6db647621 --- /dev/null +++ b/src/tools/clippy/tests/ui/const_comparisons.stderr @@ -0,0 +1,228 @@ +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:44:5 + | +LL | status_code <= 400 && status_code > 500; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `400` < `500`, the expression evaluates to false for any value of `status_code` + = note: `-D clippy::impossible-comparisons` implied by `-D warnings` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:45:5 + | +LL | status_code > 500 && status_code < 400; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `500` > `400`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:46:5 + | +LL | status_code < 500 && status_code > 500; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `status_code` cannot simultaneously be greater than and less than `500` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:49:5 + | +LL | status_code < { 400 } && status_code > { 500 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:50:5 + | +LL | status_code < STATUS_BAD_REQUEST && status_code > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:51:5 + | +LL | status_code <= u16::MIN + 1 && status_code > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:52:5 + | +LL | status_code < STATUS_SERVER_ERROR && status_code > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `status_code` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:55:5 + | +LL | status < { 400 } && status > { 500 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `{ 400 }` < `{ 500 }`, the expression evaluates to false for any value of `status` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:56:5 + | +LL | status < STATUS_BAD_REQUEST && status > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `STATUS_BAD_REQUEST` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:57:5 + | +LL | status <= u16::MIN + 1 && status > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `u16::MIN + 1` < `STATUS_SERVER_ERROR`, the expression evaluates to false for any value of `status` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:58:5 + | +LL | status < STATUS_SERVER_ERROR && status > STATUS_SERVER_ERROR; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `status` cannot simultaneously be greater than and less than `STATUS_SERVER_ERROR` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:63:5 + | +LL | 500 >= status_code && 600 < status_code; // Incorrect + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:64:5 + | +LL | 500 >= status_code && status_code > 600; // Incorrect + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `500` < `600`, the expression evaluates to false for any value of `status_code` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:69:5 + | +LL | 500 >= status && 600 < status; // Incorrect + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `500` < `600`, the expression evaluates to false for any value of `status` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:70:5 + | +LL | 500 >= status && status > 600; // Incorrect + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `500` < `600`, the expression evaluates to false for any value of `status` + +error: right-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:73:5 + | +LL | status_code < 200 && status_code <= 299; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code < 200` evaluates to true, status_code <= 299` will always evaluate to true as well + --> $DIR/const_comparisons.rs:73:23 + | +LL | status_code < 200 && status_code <= 299; + | ^^^^^^^^^^^^^^^^^^^^^ + = note: `-D clippy::redundant-comparisons` implied by `-D warnings` + +error: left-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:74:5 + | +LL | status_code > 200 && status_code >= 299; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code >= 299` evaluates to true, status_code > 200` will always evaluate to true as well + --> $DIR/const_comparisons.rs:74:5 + | +LL | status_code > 200 && status_code >= 299; + | ^^^^^^^^^^^^^^^^^^^^^ + +error: left-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:76:5 + | +LL | status_code >= 500 && status_code > 500; // Useless left + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well + --> $DIR/const_comparisons.rs:76:5 + | +LL | status_code >= 500 && status_code > 500; // Useless left + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: right-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:77:5 + | +LL | status_code > 500 && status_code >= 500; // Useless right + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code > 500` evaluates to true, status_code >= 500` will always evaluate to true as well + --> $DIR/const_comparisons.rs:77:23 + | +LL | status_code > 500 && status_code >= 500; // Useless right + | ^^^^^^^^^^^^^^^^^^^^^ + +error: left-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:78:5 + | +LL | status_code <= 500 && status_code < 500; // Useless left + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well + --> $DIR/const_comparisons.rs:78:5 + | +LL | status_code <= 500 && status_code < 500; // Useless left + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: right-hand side of `&&` operator has no effect + --> $DIR/const_comparisons.rs:79:5 + | +LL | status_code < 500 && status_code <= 500; // Useless right + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `if `status_code < 500` evaluates to true, status_code <= 500` will always evaluate to true as well + --> $DIR/const_comparisons.rs:79:23 + | +LL | status_code < 500 && status_code <= 500; // Useless right + | ^^^^^^^^^^^^^^^^^^^^^ + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:83:5 + | +LL | name < "Jennifer" && name > "Shannon"; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `"Jennifer"` < `"Shannon"`, the expression evaluates to false for any value of `name` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:86:5 + | +LL | numbers < [3, 4] && numbers > [5, 6]; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `[3, 4]` < `[5, 6]`, the expression evaluates to false for any value of `numbers` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:89:5 + | +LL | letter < 'b' && letter > 'c'; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `'b'` < `'c'`, the expression evaluates to false for any value of `letter` + +error: boolean expression will never evaluate to 'true' + --> $DIR/const_comparisons.rs:92:5 + | +LL | area < std::f32::consts::E && area > std::f32::consts::PI; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: since `std::f32::consts::E` < `std::f32::consts::PI`, the expression evaluates to false for any value of `area` + +error: aborting due to 25 previous errors + diff --git a/src/tools/clippy/tests/ui/expect.stderr b/src/tools/clippy/tests/ui/expect.stderr index be340340d477..f787fa973a32 100644 --- a/src/tools/clippy/tests/ui/expect.stderr +++ b/src/tools/clippy/tests/ui/expect.stderr @@ -4,7 +4,7 @@ error: used `expect()` on an `Option` value LL | let _ = opt.expect(""); | ^^^^^^^^^^^^^^ | - = help: if this value is `None`, it will panic + = note: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` error: used `expect()` on a `Result` value @@ -13,7 +13,7 @@ error: used `expect()` on a `Result` value LL | let _ = res.expect(""); | ^^^^^^^^^^^^^^ | - = help: if this value is an `Err`, it will panic + = note: if this value is an `Err`, it will panic error: used `expect_err()` on a `Result` value --> $DIR/expect.rs:12:13 @@ -21,7 +21,7 @@ error: used `expect_err()` on a `Result` value LL | let _ = res.expect_err(""); | ^^^^^^^^^^^^^^^^^^ | - = help: if this value is an `Ok`, it will panic + = note: if this value is an `Ok`, it will panic error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then.fixed b/src/tools/clippy/tests/ui/filter_map_bool_then.fixed index 3e72fee4b072..e5c9f783f6b3 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then.fixed +++ b/src/tools/clippy/tests/ui/filter_map_bool_then.fixed @@ -4,6 +4,7 @@ clippy::clone_on_copy, clippy::map_identity, clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_filter_map, unused )] #![warn(clippy::filter_map_bool_then)] @@ -29,9 +30,18 @@ fn main() { .copied() .filter(|&i| i != 1000) .filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1); + // Despite this is non-copy, `is_copy` still returns true (at least now) because it's `&NonCopy`, + // and any `&` is `Copy`. So since we can dereference it in `filter` (since it's then `&&NonCopy`), + // we can lint this and still get the same input type. + // See: + let v = vec![NonCopy, NonCopy]; + v.clone().iter().filter(|&i| (i == &NonCopy)).map(|i| i); // Do not lint let v = vec![NonCopy, NonCopy]; - v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); + v.clone().into_iter().filter_map(|i| (i == NonCopy).then(|| i)); + // `&mut` is `!Copy`. + let v = vec![NonCopy, NonCopy]; + v.clone().iter_mut().filter_map(|i| (i == &mut NonCopy).then(|| i)); external! { let v = vec![1, 2, 3, 4, 5, 6]; v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); @@ -42,3 +52,7 @@ fn main() { v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); } } + +fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { + iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() +} diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then.rs b/src/tools/clippy/tests/ui/filter_map_bool_then.rs index 38a04e57de45..7c9b99df78cb 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then.rs +++ b/src/tools/clippy/tests/ui/filter_map_bool_then.rs @@ -4,6 +4,7 @@ clippy::clone_on_copy, clippy::map_identity, clippy::unnecessary_lazy_evaluations, + clippy::unnecessary_filter_map, unused )] #![warn(clippy::filter_map_bool_then)] @@ -29,9 +30,18 @@ fn main() { .copied() .filter(|&i| i != 1000) .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); - // Do not lint + // Despite this is non-copy, `is_copy` still returns true (at least now) because it's `&NonCopy`, + // and any `&` is `Copy`. So since we can dereference it in `filter` (since it's then `&&NonCopy`), + // we can lint this and still get the same input type. + // See: let v = vec![NonCopy, NonCopy]; v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); + // Do not lint + let v = vec![NonCopy, NonCopy]; + v.clone().into_iter().filter_map(|i| (i == NonCopy).then(|| i)); + // `&mut` is `!Copy`. + let v = vec![NonCopy, NonCopy]; + v.clone().iter_mut().filter_map(|i| (i == &mut NonCopy).then(|| i)); external! { let v = vec![1, 2, 3, 4, 5, 6]; v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); @@ -42,3 +52,7 @@ fn main() { v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); } } + +fn issue11309<'a>(iter: impl Iterator) -> Vec<&'a str> { + iter.filter_map(|(_, s): (&str, _)| Some(s)).collect() +} diff --git a/src/tools/clippy/tests/ui/filter_map_bool_then.stderr b/src/tools/clippy/tests/ui/filter_map_bool_then.stderr index b411cd83dfd2..fffa5252e5f6 100644 --- a/src/tools/clippy/tests/ui/filter_map_bool_then.stderr +++ b/src/tools/clippy/tests/ui/filter_map_bool_then.stderr @@ -1,5 +1,5 @@ error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:19:22 + --> $DIR/filter_map_bool_then.rs:20:22 | LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` @@ -7,28 +7,34 @@ LL | v.clone().iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); = note: `-D clippy::filter-map-bool-then` implied by `-D warnings` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:20:27 + --> $DIR/filter_map_bool_then.rs:21:27 | LL | v.clone().into_iter().filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:23:10 + --> $DIR/filter_map_bool_then.rs:24:10 | LL | .filter_map(|i| -> Option<_> { (i % 2 == 0).then(|| i + 1) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:27:10 + --> $DIR/filter_map_bool_then.rs:28:10 | LL | .filter_map(|i| (i % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i % 2 == 0)).map(|i| i + 1)` error: usage of `bool::then` in `filter_map` - --> $DIR/filter_map_bool_then.rs:31:10 + --> $DIR/filter_map_bool_then.rs:32:10 | LL | .filter_map(|i| (i.clone() % 2 == 0).then(|| i + 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i.clone() % 2 == 0)).map(|i| i + 1)` -error: aborting due to 5 previous errors +error: usage of `bool::then` in `filter_map` + --> $DIR/filter_map_bool_then.rs:38:22 + | +LL | v.clone().iter().filter_map(|i| (i == &NonCopy).then(|| i)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `filter` then `map` instead: `filter(|&i| (i == &NonCopy)).map(|i| i)` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr index c567ed319b5b..19dc9071fe7b 100644 --- a/src/tools/clippy/tests/ui/get_unwrap.stderr +++ b/src/tools/clippy/tests/ui/get_unwrap.stderr @@ -16,7 +16,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise @@ -31,7 +32,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_slice.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:40:17 @@ -45,7 +47,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:41:17 @@ -59,7 +62,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vecdeque.get(0).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:42:17 @@ -73,7 +77,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_hashmap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:43:17 @@ -87,7 +92,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_btreemap.get(&1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:47:21 @@ -101,7 +107,8 @@ error: used `unwrap()` on an `Option` value LL | let _: u8 = *boxed_slice.get(1).unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:52:9 @@ -115,7 +122,8 @@ error: used `unwrap()` on an `Option` value LL | *boxed_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:53:9 @@ -129,7 +137,8 @@ error: used `unwrap()` on an `Option` value LL | *some_slice.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:54:9 @@ -143,7 +152,8 @@ error: used `unwrap()` on an `Option` value LL | *some_vec.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:55:9 @@ -157,7 +167,8 @@ error: used `unwrap()` on an `Option` value LL | *some_vecdeque.get_mut(0).unwrap() = 1; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:67:17 @@ -171,7 +182,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:68:17 @@ -185,7 +197,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = some_vec.get_mut(0..1).unwrap().to_vec(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise --> $DIR/get_unwrap.rs:78:24 diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed new file mode 100644 index 000000000000..492219fe4471 --- /dev/null +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -0,0 +1,17 @@ +//@run-rustfix + +#![warn(clippy::ignored_unit_patterns)] +#![allow(clippy::redundant_pattern_matching, clippy::single_match)] + +fn foo() -> Result<(), ()> { + unimplemented!() +} + +fn main() { + match foo() { + Ok(()) => {}, + Err(()) => {}, + } + if let Ok(()) = foo() {} + let _ = foo().map_err(|()| todo!()); +} diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs new file mode 100644 index 000000000000..90af36f8e5ed --- /dev/null +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -0,0 +1,17 @@ +//@run-rustfix + +#![warn(clippy::ignored_unit_patterns)] +#![allow(clippy::redundant_pattern_matching, clippy::single_match)] + +fn foo() -> Result<(), ()> { + unimplemented!() +} + +fn main() { + match foo() { + Ok(_) => {}, + Err(_) => {}, + } + if let Ok(_) = foo() {} + let _ = foo().map_err(|_| todo!()); +} diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr new file mode 100644 index 000000000000..8feea3cc2a83 --- /dev/null +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -0,0 +1,28 @@ +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:12:12 + | +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` + | + = note: `-D clippy::ignored-unit-patterns` implied by `-D warnings` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:13:13 + | +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:15:15 + | +LL | if let Ok(_) = foo() {} + | ^ help: use `()` instead of `_`: `()` + +error: matching over `()` is more explicit + --> $DIR/ignored_unit_patterns.rs:16:28 + | +LL | let _ = foo().map_err(|_| todo!()); + | ^ help: use `()` instead of `_`: `()` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs index 73906121c402..00661c51a78f 100644 --- a/src/tools/clippy/tests/ui/mut_reference.rs +++ b/src/tools/clippy/tests/ui/mut_reference.rs @@ -1,8 +1,21 @@ -#![allow(unused_variables)] +#![allow(unused_variables, dead_code)] fn takes_an_immutable_reference(a: &i32) {} fn takes_a_mutable_reference(a: &mut i32) {} +mod issue11268 { + macro_rules! x { + ($f:expr) => { + $f(&mut 1); + }; + } + + fn f() { + x!(super::takes_an_immutable_reference); + x!(super::takes_a_mutable_reference); + } +} + struct MyStruct; impl MyStruct { diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr index 1fdfbf9227e3..d8a71d264610 100644 --- a/src/tools/clippy/tests/ui/mut_reference.stderr +++ b/src/tools/clippy/tests/ui/mut_reference.stderr @@ -1,5 +1,5 @@ error: the function `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:17:34 + --> $DIR/mut_reference.rs:30:34 | LL | takes_an_immutable_reference(&mut 42); | ^^^^^^^ @@ -7,19 +7,19 @@ LL | takes_an_immutable_reference(&mut 42); = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings` error: the function `as_ptr` doesn't need a mutable reference - --> $DIR/mut_reference.rs:19:12 + --> $DIR/mut_reference.rs:32:12 | LL | as_ptr(&mut 42); | ^^^^^^^ error: the method `takes_an_immutable_reference` doesn't need a mutable reference - --> $DIR/mut_reference.rs:23:44 + --> $DIR/mut_reference.rs:36:44 | LL | my_struct.takes_an_immutable_reference(&mut 42); | ^^^^^^^ error: this argument is a mutable reference, but not used mutably - --> $DIR/mut_reference.rs:11:44 + --> $DIR/mut_reference.rs:24:44 | LL | fn takes_a_mutable_reference(&self, a: &mut i32) {} | ^^^^^^^^ help: consider changing to: `&i32` diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed index 26a64c861cf4..84babb974161 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed @@ -3,8 +3,22 @@ #![warn(clippy::ptr_as_ptr)] +#[macro_use] extern crate proc_macros; -use proc_macros::{external, inline_macros}; + +mod issue_11278_a { + #[derive(Debug)] + pub struct T { + pub p: D, + } +} + +mod issue_11278_b { + pub fn f(o: &mut super::issue_11278_a::T) -> super::issue_11278_a::T { + // Retain `super` + *unsafe { Box::from_raw(Box::into_raw(Box::new(o)).cast::>()) } + } +} #[inline_macros] fn main() { diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs index ea40d4947331..34fd76428b22 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs @@ -3,8 +3,22 @@ #![warn(clippy::ptr_as_ptr)] +#[macro_use] extern crate proc_macros; -use proc_macros::{external, inline_macros}; + +mod issue_11278_a { + #[derive(Debug)] + pub struct T { + pub p: D, + } +} + +mod issue_11278_b { + pub fn f(o: &mut super::issue_11278_a::T) -> super::issue_11278_a::T { + // Retain `super` + *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T) } + } +} #[inline_macros] fn main() { diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr index 78d733994ac6..e64f3351505f 100644 --- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr +++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr @@ -1,37 +1,43 @@ error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:14:13 + --> $DIR/ptr_as_ptr.rs:19:33 | -LL | let _ = ptr as *const i32; - | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` +LL | *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T) } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `Box::into_raw(Box::new(o)).cast::>()` | = note: `-D clippy::ptr-as-ptr` implied by `-D warnings` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:15:13 + --> $DIR/ptr_as_ptr.rs:28:13 + | +LL | let _ = ptr as *const i32; + | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` + +error: `as` casting between raw pointers without changing its mutability + --> $DIR/ptr_as_ptr.rs:29:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:20:17 + --> $DIR/ptr_as_ptr.rs:34:17 | LL | let _ = *ptr_ptr as *const i32; | ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:33:25 + --> $DIR/ptr_as_ptr.rs:47:25 | LL | let _: *const i32 = ptr as *const _; | ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:34:23 + --> $DIR/ptr_as_ptr.rs:48:23 | LL | let _: *mut i32 = mut_ptr as _; | ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:37:21 + --> $DIR/ptr_as_ptr.rs:51:21 | LL | let _ = inline!($ptr as *const i32); | ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::()` @@ -39,16 +45,16 @@ LL | let _ = inline!($ptr as *const i32); = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info) error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:58:13 + --> $DIR/ptr_as_ptr.rs:72:13 | LL | let _ = ptr as *const i32; | ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::()` error: `as` casting between raw pointers without changing its mutability - --> $DIR/ptr_as_ptr.rs:59:13 + --> $DIR/ptr_as_ptr.rs:73:13 | LL | let _ = mut_ptr as *mut i32; | ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::()` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed index 2d8920ccc42f..20b9e42a7aab 100644 --- a/src/tools/clippy/tests/ui/question_mark.fixed +++ b/src/tools/clippy/tests/ui/question_mark.fixed @@ -138,6 +138,23 @@ fn result_func(x: Result) -> Result { // no warning let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + // issue #11283 + // no warning + #[warn(clippy::question_mark_used)] + { + if let Err(err) = Ok(()) { + return Err(err); + } + + if Err::(0).is_err() { + return Err(0); + } else { + return Ok(0); + } + + unreachable!() + } + Ok(y) } diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs index 69451c17ee7a..8bdafd46e8fa 100644 --- a/src/tools/clippy/tests/ui/question_mark.rs +++ b/src/tools/clippy/tests/ui/question_mark.rs @@ -170,6 +170,23 @@ fn result_func(x: Result) -> Result { // no warning let _ = if let Err(e) = x { Err(e) } else { Ok(0) }; + // issue #11283 + // no warning + #[warn(clippy::question_mark_used)] + { + if let Err(err) = Ok(()) { + return Err(err); + } + + if Err::(0).is_err() { + return Err(0); + } else { + return Ok(0); + } + + unreachable!() + } + Ok(y) } diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr index 2cfd75863081..62489c8c8c47 100644 --- a/src/tools/clippy/tests/ui/question_mark.stderr +++ b/src/tools/clippy/tests/ui/question_mark.stderr @@ -115,7 +115,7 @@ LL | | } | |_____^ help: replace it with: `x?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:197:5 + --> $DIR/question_mark.rs:214:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -123,7 +123,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:204:5 + --> $DIR/question_mark.rs:221:5 | LL | / if let Err(err) = func_returning_result() { LL | | return Err(err); @@ -131,7 +131,7 @@ LL | | } | |_____^ help: replace it with: `func_returning_result()?;` error: this block may be rewritten with the `?` operator - --> $DIR/question_mark.rs:281:13 + --> $DIR/question_mark.rs:298:13 | LL | / if a.is_none() { LL | | return None; diff --git a/src/tools/clippy/tests/ui/range_contains.fixed b/src/tools/clippy/tests/ui/range_contains.fixed index 0a92ee7c8dd3..47c5248118ea 100644 --- a/src/tools/clippy/tests/ui/range_contains.fixed +++ b/src/tools/clippy/tests/ui/range_contains.fixed @@ -5,6 +5,8 @@ #![allow(clippy::no_effect)] #![allow(clippy::short_circuit_statement)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::impossible_comparisons)] +#![allow(clippy::redundant_comparisons)] fn main() { let x = 9_i32; diff --git a/src/tools/clippy/tests/ui/range_contains.rs b/src/tools/clippy/tests/ui/range_contains.rs index 7a83be609571..a35315a649d8 100644 --- a/src/tools/clippy/tests/ui/range_contains.rs +++ b/src/tools/clippy/tests/ui/range_contains.rs @@ -5,6 +5,8 @@ #![allow(clippy::no_effect)] #![allow(clippy::short_circuit_statement)] #![allow(clippy::unnecessary_operation)] +#![allow(clippy::impossible_comparisons)] +#![allow(clippy::redundant_comparisons)] fn main() { let x = 9_i32; diff --git a/src/tools/clippy/tests/ui/range_contains.stderr b/src/tools/clippy/tests/ui/range_contains.stderr index ea34023a4664..1265db695bfb 100644 --- a/src/tools/clippy/tests/ui/range_contains.stderr +++ b/src/tools/clippy/tests/ui/range_contains.stderr @@ -1,5 +1,5 @@ error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:13:5 + --> $DIR/range_contains.rs:15:5 | LL | x >= 8 && x < 12; | ^^^^^^^^^^^^^^^^ help: use: `(8..12).contains(&x)` @@ -7,121 +7,121 @@ LL | x >= 8 && x < 12; = note: `-D clippy::manual-range-contains` implied by `-D warnings` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:14:5 + --> $DIR/range_contains.rs:16:5 | LL | x < 42 && x >= 21; | ^^^^^^^^^^^^^^^^^ help: use: `(21..42).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:15:5 + --> $DIR/range_contains.rs:17:5 | LL | 100 > x && 1 <= x; | ^^^^^^^^^^^^^^^^^ help: use: `(1..100).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:18:5 + --> $DIR/range_contains.rs:20:5 | LL | x >= 9 && x <= 99; | ^^^^^^^^^^^^^^^^^ help: use: `(9..=99).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:19:5 + --> $DIR/range_contains.rs:21:5 | LL | x <= 33 && x >= 1; | ^^^^^^^^^^^^^^^^^ help: use: `(1..=33).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:20:5 + --> $DIR/range_contains.rs:22:5 | LL | 999 >= x && 1 <= x; | ^^^^^^^^^^^^^^^^^^ help: use: `(1..=999).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:23:5 + --> $DIR/range_contains.rs:25:5 | LL | x < 8 || x >= 12; | ^^^^^^^^^^^^^^^^ help: use: `!(8..12).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:24:5 + --> $DIR/range_contains.rs:26:5 | LL | x >= 42 || x < 21; | ^^^^^^^^^^^^^^^^^ help: use: `!(21..42).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:25:5 + --> $DIR/range_contains.rs:27:5 | LL | 100 <= x || 1 > x; | ^^^^^^^^^^^^^^^^^ help: use: `!(1..100).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:28:5 + --> $DIR/range_contains.rs:30:5 | LL | x < 9 || x > 99; | ^^^^^^^^^^^^^^^ help: use: `!(9..=99).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:29:5 + --> $DIR/range_contains.rs:31:5 | LL | x > 33 || x < 1; | ^^^^^^^^^^^^^^^ help: use: `!(1..=33).contains(&x)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:30:5 + --> $DIR/range_contains.rs:32:5 | LL | 999 < x || 1 > x; | ^^^^^^^^^^^^^^^^ help: use: `!(1..=999).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:45:5 + --> $DIR/range_contains.rs:47:5 | LL | y >= 0. && y < 1.; | ^^^^^^^^^^^^^^^^^ help: use: `(0. ..1.).contains(&y)` error: manual `!RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:46:5 + --> $DIR/range_contains.rs:48:5 | LL | y < 0. || y > 1.; | ^^^^^^^^^^^^^^^^ help: use: `!(0. ..=1.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:49:5 + --> $DIR/range_contains.rs:51:5 | LL | x >= -10 && x <= 10; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-10..=10).contains(&x)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:51:5 + --> $DIR/range_contains.rs:53:5 | LL | y >= -3. && y <= 3.; | ^^^^^^^^^^^^^^^^^^^ help: use: `(-3. ..=3.).contains(&y)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:56:30 + --> $DIR/range_contains.rs:58:30 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&z)` error: manual `RangeInclusive::contains` implementation - --> $DIR/range_contains.rs:56:5 + --> $DIR/range_contains.rs:58:5 | LL | (x >= 0) && (x <= 10) && (z >= 0) && (z <= 10); | ^^^^^^^^^^^^^^^^^^^^^ help: use: `(0..=10).contains(&x)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:57:29 + --> $DIR/range_contains.rs:59:29 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&z)` error: manual `!Range::contains` implementation - --> $DIR/range_contains.rs:57:5 + --> $DIR/range_contains.rs:59:5 | LL | (x < 0) || (x >= 10) || (z < 0) || (z >= 10); | ^^^^^^^^^^^^^^^^^^^^ help: use: `!(0..10).contains(&x)` error: manual `Range::contains` implementation - --> $DIR/range_contains.rs:76:5 + --> $DIR/range_contains.rs:78:5 | LL | x >= 8 && x < 35; | ^^^^^^^^^^^^^^^^ help: use: `(8..35).contains(&x)` diff --git a/src/tools/clippy/tests/ui/redundant_guards.fixed b/src/tools/clippy/tests/ui/redundant_guards.fixed index 77ac76668649..49d7336ee371 100644 --- a/src/tools/clippy/tests/ui/redundant_guards.fixed +++ b/src/tools/clippy/tests/ui/redundant_guards.fixed @@ -15,6 +15,19 @@ struct B { struct C(u32, u32); +#[derive(PartialEq)] +struct FloatWrapper(f32); +fn issue11304() { + match 0.1 { + x if x == 0.0 => todo!(), + _ => todo!(), + } + match FloatWrapper(0.1) { + x if x == FloatWrapper(0.0) => todo!(), + _ => todo!(), + } +} + fn main() { let c = C(1, 2); match c { diff --git a/src/tools/clippy/tests/ui/redundant_guards.rs b/src/tools/clippy/tests/ui/redundant_guards.rs index b072e4ea14d1..87761010de2c 100644 --- a/src/tools/clippy/tests/ui/redundant_guards.rs +++ b/src/tools/clippy/tests/ui/redundant_guards.rs @@ -15,6 +15,19 @@ struct B { struct C(u32, u32); +#[derive(PartialEq)] +struct FloatWrapper(f32); +fn issue11304() { + match 0.1 { + x if x == 0.0 => todo!(), + _ => todo!(), + } + match FloatWrapper(0.1) { + x if x == FloatWrapper(0.0) => todo!(), + _ => todo!(), + } +} + fn main() { let c = C(1, 2); match c { diff --git a/src/tools/clippy/tests/ui/redundant_guards.stderr b/src/tools/clippy/tests/ui/redundant_guards.stderr index c2a92071d1df..5bdf43d23c53 100644 --- a/src/tools/clippy/tests/ui/redundant_guards.stderr +++ b/src/tools/clippy/tests/ui/redundant_guards.stderr @@ -1,5 +1,5 @@ error: redundant guard - --> $DIR/redundant_guards.rs:21:20 + --> $DIR/redundant_guards.rs:34:20 | LL | C(x, y) if let 1 = y => .., | ^^^^^^^^^ @@ -12,7 +12,7 @@ LL + C(x, 1) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:27:20 + --> $DIR/redundant_guards.rs:40:20 | LL | Some(x) if matches!(x, Some(1) if true) => .., | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | Some(Some(1)) if true => .., | ~~~~~~~ ~~~~~~~ error: redundant guard - --> $DIR/redundant_guards.rs:28:20 + --> $DIR/redundant_guards.rs:41:20 | LL | Some(x) if matches!(x, Some(1)) => { | ^^^^^^^^^^^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + Some(Some(1)) => { | error: redundant guard - --> $DIR/redundant_guards.rs:32:20 + --> $DIR/redundant_guards.rs:45:20 | LL | Some(x) if let Some(1) = x => .., | ^^^^^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL + Some(Some(1)) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:33:20 + --> $DIR/redundant_guards.rs:46:20 | LL | Some(x) if x == Some(2) => .., | ^^^^^^^^^^^^ @@ -59,7 +59,7 @@ LL + Some(Some(2)) => .., | error: redundant guard - --> $DIR/redundant_guards.rs:56:20 + --> $DIR/redundant_guards.rs:69:20 | LL | B { e } if matches!(e, Some(A(2))) => .., | ^^^^^^^^^^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL + B { e: Some(A(2)) } => .., | error: redundant guard - --> $DIR/redundant_guards.rs:93:20 + --> $DIR/redundant_guards.rs:106:20 | LL | E::A(y) if y == "not from an or pattern" => {}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,7 +83,7 @@ LL + E::A("not from an or pattern") => {}, | error: redundant guard - --> $DIR/redundant_guards.rs:100:14 + --> $DIR/redundant_guards.rs:113:14 | LL | x if matches!(x, Some(0)) => .., | ^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/redundant_locals.rs b/src/tools/clippy/tests/ui/redundant_locals.rs index e74c78a50b67..80af38f47b86 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.rs +++ b/src/tools/clippy/tests/ui/redundant_locals.rs @@ -27,6 +27,15 @@ fn downgraded_mutability() { let x = x; } +// see #11290 +fn shadow_mutation() { + let mut x = 1; + { + let mut x = x; + x = 2; + } +} + fn coercion(par: &mut i32) { let par: &i32 = par; diff --git a/src/tools/clippy/tests/ui/redundant_locals.stderr b/src/tools/clippy/tests/ui/redundant_locals.stderr index df07dd2f453b..587de0575247 100644 --- a/src/tools/clippy/tests/ui/redundant_locals.stderr +++ b/src/tools/clippy/tests/ui/redundant_locals.stderr @@ -20,7 +20,7 @@ LL | let mut x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:37:14 + --> $DIR/redundant_locals.rs:46:14 | LL | fn parameter(x: i32) { | ^ @@ -30,7 +30,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:42:9 + --> $DIR/redundant_locals.rs:51:9 | LL | let x = 1; | ^ @@ -40,7 +40,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:43:9 + --> $DIR/redundant_locals.rs:52:9 | LL | let x = x; | ^ @@ -50,7 +50,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:44:9 + --> $DIR/redundant_locals.rs:53:9 | LL | let x = x; | ^ @@ -60,7 +60,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:45:9 + --> $DIR/redundant_locals.rs:54:9 | LL | let x = x; | ^ @@ -70,7 +70,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:50:9 + --> $DIR/redundant_locals.rs:59:9 | LL | let a = 1; | ^ @@ -81,7 +81,7 @@ LL | let a = a; = help: remove the redefinition of `a` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:51:9 + --> $DIR/redundant_locals.rs:60:9 | LL | let b = 2; | ^ @@ -92,7 +92,7 @@ LL | let b = b; = help: remove the redefinition of `b` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:58:13 + --> $DIR/redundant_locals.rs:67:13 | LL | let x = 1; | ^ @@ -102,7 +102,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:65:13 + --> $DIR/redundant_locals.rs:74:13 | LL | let x = 1; | ^ @@ -112,7 +112,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:68:6 + --> $DIR/redundant_locals.rs:77:6 | LL | |x: i32| { | ^ @@ -122,7 +122,7 @@ LL | let x = x; = help: remove the redefinition of `x` error: redundant redefinition of a binding - --> $DIR/redundant_locals.rs:85:9 + --> $DIR/redundant_locals.rs:94:9 | LL | let x = 1; | ^ diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.rs b/src/tools/clippy/tests/ui/redundant_type_annotations.rs index cc507b8d658c..09dbd3c9b39e 100644 --- a/src/tools/clippy/tests/ui/redundant_type_annotations.rs +++ b/src/tools/clippy/tests/ui/redundant_type_annotations.rs @@ -6,8 +6,8 @@ struct Cake { _data: T, } -fn make_something() -> T { - T::default() +fn make_something() -> T { + unimplemented!() } fn make_cake() -> Cake { @@ -117,7 +117,15 @@ fn test_non_locals() { let _closure_arg = |x: u32| x; } -fn test_complex_types() { +trait Trait { + type AssocTy; +} + +impl Trait for () { + type AssocTy = String; +} + +fn test_complex_types() { // Shouldn't be lint, since the literal will be i32 otherwise let _u8: u8 = 128; @@ -135,6 +143,10 @@ fn test_complex_types() { // Shouldn't be lint let _array: [u32; 2] = [8, 9]; + + let ty_param: T = make_something(); + + let assoc_ty: <() as Trait>::AssocTy = String::new(); } fn test_functions() { @@ -173,4 +185,6 @@ fn test_simple_types() { let _var: bool = false; } +fn issue11190() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.stderr b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr index e8b2fe5c3847..988ebe637226 100644 --- a/src/tools/clippy/tests/ui/redundant_type_annotations.stderr +++ b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr @@ -19,85 +19,85 @@ LL | let v: &Slice = self.return_a_ref_to_struct(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:143:5 + --> $DIR/redundant_type_annotations.rs:155:5 | LL | let _return: String = return_a_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:145:5 + --> $DIR/redundant_type_annotations.rs:157:5 | LL | let _return: Pie = return_a_struct(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:147:5 + --> $DIR/redundant_type_annotations.rs:159:5 | LL | let _return: Pizza = return_an_enum(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:149:5 + --> $DIR/redundant_type_annotations.rs:161:5 | LL | let _return: u32 = return_an_int(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:151:5 + --> $DIR/redundant_type_annotations.rs:163:5 | LL | let _return: String = String::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:153:5 + --> $DIR/redundant_type_annotations.rs:165:5 | LL | let new_pie: Pie = Pie::new(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:155:5 + --> $DIR/redundant_type_annotations.rs:167:5 | LL | let _return: u32 = new_pie.return_an_int(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:157:5 + --> $DIR/redundant_type_annotations.rs:169:5 | LL | let _return: u32 = Pie::associated_return_an_int(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:159:5 + --> $DIR/redundant_type_annotations.rs:171:5 | LL | let _return: String = Pie::associated_return_a_string(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:165:5 + --> $DIR/redundant_type_annotations.rs:177:5 | LL | let _var: u32 = u32::MAX; | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:167:5 + --> $DIR/redundant_type_annotations.rs:179:5 | LL | let _var: u32 = 5_u32; | ^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:169:5 + --> $DIR/redundant_type_annotations.rs:181:5 | LL | let _var: &str = "test"; | ^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:171:5 + --> $DIR/redundant_type_annotations.rs:183:5 | LL | let _var: &[u8] = b"test"; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: redundant type annotation - --> $DIR/redundant_type_annotations.rs:173:5 + --> $DIR/redundant_type_annotations.rs:185:5 | LL | let _var: bool = false; | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed index 8257bf2947ab..e78b9e5c9c12 100644 --- a/src/tools/clippy/tests/ui/rename.fixed +++ b/src/tools/clippy/tests/ui/rename.fixed @@ -29,9 +29,9 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] +#![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] -#![allow(invalid_reference_casting)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs index 6569dad18d40..2e6ef60cb798 100644 --- a/src/tools/clippy/tests/ui/rename.rs +++ b/src/tools/clippy/tests/ui/rename.rs @@ -29,9 +29,9 @@ #![allow(clippy::recursive_format_impl)] #![allow(clippy::unwrap_or_default)] #![allow(clippy::invisible_characters)] +#![allow(invalid_reference_casting)] #![allow(suspicious_double_ref_op)] #![allow(invalid_nan_comparisons)] -#![allow(invalid_reference_casting)] #![allow(drop_bounds)] #![allow(dropping_copy_types)] #![allow(dropping_references)] diff --git a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr index 8bb3c8fbeebd..d93a55ba9065 100644 --- a/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr +++ b/src/tools/clippy/tests/ui/suspicious_xor_used_as_pow.stderr @@ -10,31 +10,31 @@ error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:20:13 | LL | let _ = 2i32 ^ 9i32; - | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(9_i32)` + | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(9i32)` error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:21:13 | LL | let _ = 2i32 ^ 2i32; - | ^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(2_i32)` + | ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(2i32)` error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:22:13 | LL | let _ = 50i32 ^ 3i32; - | ^^^^^^^^^^^^ help: did you mean to write: `50_i32.pow(3_i32)` + | ^^^^^^^^^^^^ help: did you mean to write: `50i32.pow(3i32)` error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:23:13 | LL | let _ = 5i32 ^ 8i32; - | ^^^^^^^^^^^ help: did you mean to write: `5_i32.pow(8_i32)` + | ^^^^^^^^^^^ help: did you mean to write: `5i32.pow(8i32)` error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:24:13 | LL | let _ = 2i32 ^ 32i32; - | ^^^^^^^^^^^^ help: did you mean to write: `2_i32.pow(32_i32)` + | ^^^^^^^^^^^^ help: did you mean to write: `2i32.pow(32i32)` error: `^` is not the exponentiation operator --> $DIR/suspicious_xor_used_as_pow.rs:13:9 diff --git a/src/tools/clippy/tests/ui/unwrap.stderr b/src/tools/clippy/tests/ui/unwrap.stderr index 3796d942ff9f..41db819f6fb7 100644 --- a/src/tools/clippy/tests/ui/unwrap.stderr +++ b/src/tools/clippy/tests/ui/unwrap.stderr @@ -4,7 +4,8 @@ error: used `unwrap()` on an `Option` value LL | let _ = opt.unwrap(); | ^^^^^^^^^^^^ | - = help: if you don't want to handle the `None` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is `None`, it will panic + = help: consider using `expect()` to provide a better panic message = note: `-D clippy::unwrap-used` implied by `-D warnings` error: used `unwrap()` on a `Result` value @@ -13,7 +14,8 @@ error: used `unwrap()` on a `Result` value LL | let _ = res.unwrap(); | ^^^^^^^^^^^^ | - = help: if you don't want to handle the `Err` case gracefully, consider using `expect()` to provide a better panic message + = note: if this value is an `Err`, it will panic + = help: consider using `expect()` to provide a better panic message error: used `unwrap_err()` on a `Result` value --> $DIR/unwrap.rs:12:13 @@ -21,7 +23,8 @@ error: used `unwrap_err()` on a `Result` value LL | let _ = res.unwrap_err(); | ^^^^^^^^^^^^^^^^ | - = help: if you don't want to handle the `Ok` case gracefully, consider using `expect_err()` to provide a better panic message + = note: if this value is an `Ok`, it will panic + = help: consider using `expect_err()` to provide a better panic message error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.rs b/src/tools/clippy/tests/ui/unwrap_expect_used.rs index 7f57efc53c9c..26f92ccdefaa 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.rs +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.rs @@ -1,5 +1,8 @@ #![warn(clippy::unwrap_used, clippy::expect_used)] #![allow(clippy::unnecessary_literal_unwrap)] +#![feature(never_type)] + +use std::convert::Infallible; trait OptionExt { type Item; @@ -28,6 +31,14 @@ fn main() { Some(3).unwrap_err(); Some(3).expect_err("Hellow none!"); + // Issue #11245: The `Err` variant can never be constructed so do not lint this. + let x: Result<(), !> = Ok(()); + x.unwrap(); + x.expect("is `!` (never)"); + let x: Result<(), Infallible> = Ok(()); + x.unwrap(); + x.expect("is never-like (0 variants)"); + let a: Result = Ok(3); a.unwrap(); a.expect("Hello world!"); diff --git a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr index 1a551ab5ab8e..f66e47612add 100644 --- a/src/tools/clippy/tests/ui/unwrap_expect_used.stderr +++ b/src/tools/clippy/tests/ui/unwrap_expect_used.stderr @@ -1,52 +1,52 @@ error: used `unwrap()` on an `Option` value - --> $DIR/unwrap_expect_used.rs:24:5 + --> $DIR/unwrap_expect_used.rs:27:5 | LL | Some(3).unwrap(); | ^^^^^^^^^^^^^^^^ | - = help: if this value is `None`, it will panic + = note: if this value is `None`, it will panic = note: `-D clippy::unwrap-used` implied by `-D warnings` error: used `expect()` on an `Option` value - --> $DIR/unwrap_expect_used.rs:25:5 + --> $DIR/unwrap_expect_used.rs:28:5 | LL | Some(3).expect("Hello world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if this value is `None`, it will panic + = note: if this value is `None`, it will panic = note: `-D clippy::expect-used` implied by `-D warnings` error: used `unwrap()` on a `Result` value - --> $DIR/unwrap_expect_used.rs:32:5 + --> $DIR/unwrap_expect_used.rs:43:5 | LL | a.unwrap(); | ^^^^^^^^^^ | - = help: if this value is an `Err`, it will panic + = note: if this value is an `Err`, it will panic error: used `expect()` on a `Result` value - --> $DIR/unwrap_expect_used.rs:33:5 + --> $DIR/unwrap_expect_used.rs:44:5 | LL | a.expect("Hello world!"); | ^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if this value is an `Err`, it will panic + = note: if this value is an `Err`, it will panic error: used `unwrap_err()` on a `Result` value - --> $DIR/unwrap_expect_used.rs:34:5 + --> $DIR/unwrap_expect_used.rs:45:5 | LL | a.unwrap_err(); | ^^^^^^^^^^^^^^ | - = help: if this value is an `Ok`, it will panic + = note: if this value is an `Ok`, it will panic error: used `expect_err()` on a `Result` value - --> $DIR/unwrap_expect_used.rs:35:5 + --> $DIR/unwrap_expect_used.rs:46:5 | LL | a.expect_err("Hello error!"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = help: if this value is an `Ok`, it will panic + = note: if this value is an `Ok`, it will panic error: aborting due to 6 previous errors diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs index 1ec4cbc4de71..97718f1f4a9f 100644 --- a/src/tools/miri/src/bin/miri.rs +++ b/src/tools/miri/src/bin/miri.rs @@ -69,7 +69,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls { } init_late_loggers(handler, tcx); - if !tcx.sess.crate_types().contains(&CrateType::Executable) { + if !tcx.crate_types().contains(&CrateType::Executable) { tcx.sess.fatal("miri only makes sense on bin crates"); } diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 22e9b46347a5..a9ab696cdf4d 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -113,7 +113,6 @@ const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!()); /// rustc. Please check with the compiler team before adding an entry. const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[ // tidy-alphabetical-start - "addr2line", "adler", "ahash", "aho-corasick", diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs index b386b000beff..3a4d9c53d7bc 100644 --- a/src/tools/tidy/src/pal.rs +++ b/src/tools/tidy/src/pal.rs @@ -52,6 +52,7 @@ const EXCEPTION_PATHS: &[&str] = &[ // FIXME: platform-specific code should be moved to `sys` "library/std/src/io/copy.rs", "library/std/src/io/stdio.rs", + "library/std/src/lib.rs", // for miniz_oxide leaking docs, which itself workaround "library/std/src/path.rs", "library/std/src/sys_common", // Should only contain abstractions over platforms "library/std/src/net/test.rs", // Utility helpers for tests diff --git a/tests/codegen/method-declaration.rs b/tests/codegen/method-declaration.rs new file mode 100644 index 000000000000..4ae332b0107b --- /dev/null +++ b/tests/codegen/method-declaration.rs @@ -0,0 +1,26 @@ +// compile-flags: -g -Cno-prepopulate-passes + +// Verify that we added a declaration for a method. + +// CHECK: define{{.*}}@method{{.*}} !dbg ![[METHOD_DEF_DBG:[0-9]+]] +// CHECK: define{{.*}}@function{{.*}} !dbg ![[FUNC_DEF_DBG:[0-9]+]] + +#![crate_type = "lib"] + +// CHECK-DAG: ![[FOO_DBG:[0-9]+]] = !DICompositeType(tag: {{.*}} name: "Foo", {{.*}} identifier: +pub struct Foo; + +impl Foo { + // CHECK-DAG: ![[METHOD_DEF_DBG]] = distinct !DISubprogram(name: "method"{{.*}}, scope: ![[FOO_DBG]]{{.*}}DISPFlagDefinition{{.*}}, declaration: ![[METHOD_DECL_DBG:[0-9]+]] + // CHECK-DAG: ![[METHOD_DECL_DBG]] = !DISubprogram(name: "method"{{.*}}, scope: ![[FOO_DBG]] + #[no_mangle] + pub fn method() {} +} + +// CHECK: ![[FUNC_DEF_DBG]] = distinct !DISubprogram(name: "function" +// CHECK-NOT: declaration +// CHECK-SAME: DISPFlagDefinition +// CHECK-NOT: declaration +// CHECK-SAME: ) +#[no_mangle] +pub fn function() {} diff --git a/tests/codegen/ptr-arithmetic.rs b/tests/codegen/ptr-arithmetic.rs new file mode 100644 index 000000000000..292bfdaf3571 --- /dev/null +++ b/tests/codegen/ptr-arithmetic.rs @@ -0,0 +1,34 @@ +// compile-flags: -O -Z merge-functions=disabled +// ignore-debug (the extra assertions get in the way) + +#![crate_type = "lib"] + +// CHECK-LABEL: ptr @i32_add( +// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n) +#[no_mangle] +pub unsafe fn i32_add(p: *const i32, n: usize) -> *const i32 { + // CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %n + // CHECK: ret ptr %[[TEMP]] + p.add(n) +} + +// Ensure we tell LLVM that the negation in `sub` can't overflow. + +// CHECK-LABEL: ptr @i32_sub( +// CHECK-SAME: [[WORD:i[0-9]+]] noundef %n) +#[no_mangle] +pub unsafe fn i32_sub(p: *const i32, n: usize) -> *const i32 { + // CHECK: %[[DELTA:.+]] = sub nsw [[WORD]] 0, %n + // CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %[[DELTA]] + // CHECK: ret ptr %[[TEMP]] + p.sub(n) +} + +// CHECK-LABEL: ptr @i32_offset( +// CHECK-SAME: [[WORD:i[0-9]+]] noundef %d) +#[no_mangle] +pub unsafe fn i32_offset(p: *const i32, d: isize) -> *const i32 { + // CHECK: %[[TEMP:.+]] = getelementptr inbounds i32, ptr %p, [[WORD]] %d + // CHECK: ret ptr %[[TEMP]] + p.offset(d) +} diff --git a/tests/ui/async-await/in-trait/return-type-suggestion.rs b/tests/ui/async-await/in-trait/return-type-suggestion.rs index 92a9e035e89d..cdab4ea0f371 100644 --- a/tests/ui/async-await/in-trait/return-type-suggestion.rs +++ b/tests/ui/async-await/in-trait/return-type-suggestion.rs @@ -6,7 +6,6 @@ trait A { async fn e() { Ok(()) //~^ ERROR mismatched types - //~| HELP consider using a semicolon here } } diff --git a/tests/ui/async-await/in-trait/return-type-suggestion.stderr b/tests/ui/async-await/in-trait/return-type-suggestion.stderr index d93094598125..179c9ed93db3 100644 --- a/tests/ui/async-await/in-trait/return-type-suggestion.stderr +++ b/tests/ui/async-await/in-trait/return-type-suggestion.stderr @@ -2,9 +2,7 @@ error[E0308]: mismatched types --> $DIR/return-type-suggestion.rs:7:9 | LL | Ok(()) - | ^^^^^^- help: consider using a semicolon here: `;` - | | - | expected `()`, found `Result<(), _>` + | ^^^^^^ expected `()`, found `Result<(), _>` | = note: expected unit type `()` found enum `Result<(), _>` diff --git a/tests/ui/fmt/suggest-inline-args.rs b/tests/ui/fmt/suggest-inline-args.rs new file mode 100644 index 000000000000..515335ed9f40 --- /dev/null +++ b/tests/ui/fmt/suggest-inline-args.rs @@ -0,0 +1,28 @@ +mod foo { + pub fn bar() -> i32 { + 1 + } +} + +fn bar() -> i32 { + 2 +} + +fn main() { + let stderr = 3; + eprintln!({stderr}); + //~^ ERROR format argument must be a string literal + //~| HELP quote your inlined format argument to use as string literal + eprintln!({1}); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with + eprintln!({foo::bar()}); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with + eprintln!({bar()}); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with + eprintln!({1; 2}); + //~^ ERROR format argument must be a string literal + //~| HELP you might be missing a string literal to format with +} diff --git a/tests/ui/fmt/suggest-inline-args.stderr b/tests/ui/fmt/suggest-inline-args.stderr new file mode 100644 index 000000000000..cf70568cf3f9 --- /dev/null +++ b/tests/ui/fmt/suggest-inline-args.stderr @@ -0,0 +1,57 @@ +error: format argument must be a string literal + --> $DIR/suggest-inline-args.rs:13:15 + | +LL | eprintln!({stderr}); + | ^^^^^^^^ + | +help: quote your inlined format argument to use as string literal + | +LL | eprintln!("{stderr}"); + | + + + +error: format argument must be a string literal + --> $DIR/suggest-inline-args.rs:16:15 + | +LL | eprintln!({1}); + | ^^^ + | +help: you might be missing a string literal to format with + | +LL | eprintln!("{}", {1}); + | +++++ + +error: format argument must be a string literal + --> $DIR/suggest-inline-args.rs:19:15 + | +LL | eprintln!({foo::bar()}); + | ^^^^^^^^^^^^ + | +help: you might be missing a string literal to format with + | +LL | eprintln!("{}", {foo::bar()}); + | +++++ + +error: format argument must be a string literal + --> $DIR/suggest-inline-args.rs:22:15 + | +LL | eprintln!({bar()}); + | ^^^^^^^ + | +help: you might be missing a string literal to format with + | +LL | eprintln!("{}", {bar()}); + | +++++ + +error: format argument must be a string literal + --> $DIR/suggest-inline-args.rs:25:15 + | +LL | eprintln!({1; 2}); + | ^^^^^^ + | +help: you might be missing a string literal to format with + | +LL | eprintln!("{}", {1; 2}); + | +++++ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs b/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs new file mode 100644 index 000000000000..8dcc35a281a7 --- /dev/null +++ b/tests/ui/implied-bounds/implied_bounds_entailment_skip_non_outlives.rs @@ -0,0 +1,23 @@ +// check-pass +// See issue #109356. We don't want a false positive to the `implied_bounds_entailment` lint. + +use std::borrow::Cow; + +pub trait Trait { + fn method(self) -> Option> + where + Self: Sized; +} + +impl<'a> Trait for Cow<'a, str> { + // If we're not careful here, we'll check `WF(return-type)` using the trait + // and impl where clauses, requiring that `Cow<'a, str>: Sized`. This is + // obviously true, but if we pick the `Self: Sized` clause from the trait + // over the "inherent impl", we will require `'a == 'static`, which triggers + // the `implied_bounds_entailment` lint. + fn method(self) -> Option> { + None + } +} + +fn main() {} diff --git a/tests/ui/implied-bounds/trait-where-clause-implied.rs b/tests/ui/implied-bounds/trait-where-clause-implied.rs new file mode 100644 index 000000000000..5f9ab66d3c89 --- /dev/null +++ b/tests/ui/implied-bounds/trait-where-clause-implied.rs @@ -0,0 +1,15 @@ +// check-pass + +pub trait Trait<'a, 'b> { + fn method(self, _: &'static &'static ()) + where + 'a: 'b; +} + +impl<'a> Trait<'a, 'static> for () { + // On first glance, this seems like we have the extra implied bound that + // `'a: 'static`, but we know this from the trait method where clause. + fn method(self, _: &'static &'a ()) {} +} + +fn main() {} diff --git a/tests/ui/lazy-type-alias/leading-where-clause.fixed b/tests/ui/lazy-type-alias/leading-where-clause.fixed new file mode 100644 index 000000000000..07ebc09b30ee --- /dev/null +++ b/tests/ui/lazy-type-alias/leading-where-clause.fixed @@ -0,0 +1,15 @@ +// run-rustfix + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// Check that we *reject* leading where-clauses on lazy type aliases. + +type Alias + += T where String: From; +//~^^^ ERROR where clauses are not allowed before the type for type aliases + +fn main() { + let _: Alias<&str>; +} diff --git a/tests/ui/lazy-type-alias/leading-where-clause.rs b/tests/ui/lazy-type-alias/leading-where-clause.rs new file mode 100644 index 000000000000..4a6542934720 --- /dev/null +++ b/tests/ui/lazy-type-alias/leading-where-clause.rs @@ -0,0 +1,16 @@ +// run-rustfix + +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// Check that we *reject* leading where-clauses on lazy type aliases. + +type Alias +where + String: From, += T; +//~^^^ ERROR where clauses are not allowed before the type for type aliases + +fn main() { + let _: Alias<&str>; +} diff --git a/tests/ui/lazy-type-alias/leading-where-clause.stderr b/tests/ui/lazy-type-alias/leading-where-clause.stderr new file mode 100644 index 000000000000..8ddf0ce6c655 --- /dev/null +++ b/tests/ui/lazy-type-alias/leading-where-clause.stderr @@ -0,0 +1,16 @@ +error: where clauses are not allowed before the type for type aliases + --> $DIR/leading-where-clause.rs:9:1 + | +LL | / where +LL | | String: From, + | |____________________^ + | + = note: see issue #89122 for more information +help: move it to the end of the type declaration + | +LL + +LL ~ = T where String: From; + | + +error: aborting due to previous error + diff --git a/tests/ui/lazy-type-alias/trailing-where-clause.rs b/tests/ui/lazy-type-alias/trailing-where-clause.rs new file mode 100644 index 000000000000..ac9598fe5f6a --- /dev/null +++ b/tests/ui/lazy-type-alias/trailing-where-clause.rs @@ -0,0 +1,13 @@ +#![feature(lazy_type_alias)] +#![allow(incomplete_features)] + +// Check that we allow & respect trailing where-clauses on lazy type aliases. + +type Alias = T +where + String: From; + +fn main() { + let _: Alias<&str>; + let _: Alias<()>; //~ ERROR the trait bound `String: From<()>` is not satisfied +} diff --git a/tests/ui/lazy-type-alias/trailing-where-clause.stderr b/tests/ui/lazy-type-alias/trailing-where-clause.stderr new file mode 100644 index 000000000000..d7606ba6b2a9 --- /dev/null +++ b/tests/ui/lazy-type-alias/trailing-where-clause.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `String: From<()>` is not satisfied + --> $DIR/trailing-where-clause.rs:12:12 + | +LL | let _: Alias<()>; + | ^^^^^^^^^ the trait `From<()>` is not implemented for `String` + | + = help: the following other types implement trait `From`: + > + >> + >> + > + > + > +note: required by a bound on the type alias `Alias` + --> $DIR/trailing-where-clause.rs:8:13 + | +LL | String: From; + | ^^^^^^^ required by this bound + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/return/return-struct.rs b/tests/ui/return/return-struct.rs new file mode 100644 index 000000000000..446445c17bae --- /dev/null +++ b/tests/ui/return/return-struct.rs @@ -0,0 +1,24 @@ +struct S; + +enum Age { + Years(i64, i64) +} + +fn foo() { + let mut age = 29; + Age::Years({age += 1; age}, 55) + //~^ ERROR mismatched types +} + +fn bar() { + let mut age = 29; + Age::Years(age, 55) + //~^ ERROR mismatched types +} + +fn baz() { + S + //~^ ERROR mismatched types +} + +fn main() {} diff --git a/tests/ui/return/return-struct.stderr b/tests/ui/return/return-struct.stderr new file mode 100644 index 000000000000..e6c0363e3631 --- /dev/null +++ b/tests/ui/return/return-struct.stderr @@ -0,0 +1,35 @@ +error[E0308]: mismatched types + --> $DIR/return-struct.rs:9:5 + | +LL | Age::Years({age += 1; age}, 55) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `Age` + | +help: consider using a semicolon here + | +LL | Age::Years({age += 1; age}, 55); + | + +help: try adding a return type + | +LL | fn foo() -> Age { + | ++++++ + +error[E0308]: mismatched types + --> $DIR/return-struct.rs:15:5 + | +LL | fn bar() { + | - help: try adding a return type: `-> Age` +LL | let mut age = 29; +LL | Age::Years(age, 55) + | ^^^^^^^^^^^^^^^^^^^ expected `()`, found `Age` + +error[E0308]: mismatched types + --> $DIR/return-struct.rs:20:5 + | +LL | fn baz() { + | - help: try adding a return type: `-> S` +LL | S + | ^ expected `()`, found `S` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/sanitize/issue-114275-cfi-const-expr-in-arry-len.rs b/tests/ui/sanitize/issue-114275-cfi-const-expr-in-arry-len.rs new file mode 100644 index 000000000000..8f870be13725 --- /dev/null +++ b/tests/ui/sanitize/issue-114275-cfi-const-expr-in-arry-len.rs @@ -0,0 +1,15 @@ +// Regression test for issue 114275 `typeid::typeid_itanium_cxx_abi::transform_ty` +// was expecting array type lengths to be evaluated, this was causing an ICE. +// +// build-pass +// compile-flags: -Ccodegen-units=1 -Clto -Zsanitizer=cfi -Ctarget-feature=-crt-static +// needs-sanitizer-cfi + +#![crate_type = "lib"] + +#[repr(transparent)] +pub struct Array([u8; 1 * 1]); + +pub extern "C" fn array() -> Array { + loop {} +} diff --git a/tests/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr b/tests/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr index 438075083d37..bb8b9b37067e 100644 --- a/tests/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr +++ b/tests/ui/suggestions/issue-51055-missing-semicolon-between-call-and-tuple.stderr @@ -5,7 +5,7 @@ LL | fn vindictive() -> bool { true } | ----------------------- `vindictive` defined here returns `bool` ... LL | vindictive() - | -^^^^^^^^^^^- help: consider using a semicolon here: `;` + | -^^^^^^^^^^^- help: consider using a semicolon here to finish the statement: `;` | _____| | | LL | | (1, 2) diff --git a/tests/ui/suggestions/missing-semicolon.fixed b/tests/ui/suggestions/missing-semicolon.fixed new file mode 100644 index 000000000000..df355f0b0076 --- /dev/null +++ b/tests/ui/suggestions/missing-semicolon.fixed @@ -0,0 +1,38 @@ +// run-rustfix +#![allow(dead_code, unused_variables, path_statements)] +fn a() { + let x = 5; + let y = x; //~ ERROR expected function + (); //~ ERROR expected `;`, found `}` +} + +fn b() { + let x = 5; + let y = x; //~ ERROR expected function + (); +} +fn c() { + let x = 5; + x; //~ ERROR expected function + () +} +fn d() { // ok + let x = || (); + x + () +} +fn e() { // ok + let x = || (); + x + (); +} +fn f() + { + let y = 5; //~ ERROR expected function + (); //~ ERROR expected `;`, found `}` +} +fn g() { + 5; //~ ERROR expected function + (); +} +fn main() {} diff --git a/tests/ui/suggestions/missing-semicolon.rs b/tests/ui/suggestions/missing-semicolon.rs new file mode 100644 index 000000000000..12ef3d33e5f4 --- /dev/null +++ b/tests/ui/suggestions/missing-semicolon.rs @@ -0,0 +1,38 @@ +// run-rustfix +#![allow(dead_code, unused_variables, path_statements)] +fn a() { + let x = 5; + let y = x //~ ERROR expected function + () //~ ERROR expected `;`, found `}` +} + +fn b() { + let x = 5; + let y = x //~ ERROR expected function + (); +} +fn c() { + let x = 5; + x //~ ERROR expected function + () +} +fn d() { // ok + let x = || (); + x + () +} +fn e() { // ok + let x = || (); + x + (); +} +fn f() + { + let y = 5 //~ ERROR expected function + () //~ ERROR expected `;`, found `}` +} +fn g() { + 5 //~ ERROR expected function + (); +} +fn main() {} diff --git a/tests/ui/suggestions/missing-semicolon.stderr b/tests/ui/suggestions/missing-semicolon.stderr new file mode 100644 index 000000000000..54a64f664b50 --- /dev/null +++ b/tests/ui/suggestions/missing-semicolon.stderr @@ -0,0 +1,75 @@ +error: expected `;`, found `}` + --> $DIR/missing-semicolon.rs:6:7 + | +LL | () + | ^ help: add `;` here +LL | } + | - unexpected token + +error: expected `;`, found `}` + --> $DIR/missing-semicolon.rs:32:7 + | +LL | () + | ^ help: add `;` here +LL | } + | - unexpected token + +error[E0618]: expected function, found `{integer}` + --> $DIR/missing-semicolon.rs:5:13 + | +LL | let x = 5; + | - `x` has type `{integer}` +LL | let y = x + | ^- help: consider using a semicolon here to finish the statement: `;` + | _____________| + | | +LL | | () + | |______- call expression requires function + +error[E0618]: expected function, found `{integer}` + --> $DIR/missing-semicolon.rs:11:13 + | +LL | let x = 5; + | - `x` has type `{integer}` +LL | let y = x + | ^- help: consider using a semicolon here to finish the statement: `;` + | _____________| + | | +LL | | (); + | |______- call expression requires function + +error[E0618]: expected function, found `{integer}` + --> $DIR/missing-semicolon.rs:16:5 + | +LL | let x = 5; + | - `x` has type `{integer}` +LL | x + | ^- help: consider using a semicolon here to finish the statement: `;` + | _____| + | | +LL | | () + | |______- call expression requires function + +error[E0618]: expected function, found `{integer}` + --> $DIR/missing-semicolon.rs:31:13 + | +LL | let y = 5 + | ^- help: consider using a semicolon here to finish the statement: `;` + | _____________| + | | +LL | | () + | |______- call expression requires function + +error[E0618]: expected function, found `{integer}` + --> $DIR/missing-semicolon.rs:35:5 + | +LL | 5 + | ^- help: consider using a semicolon here to finish the statement: `;` + | _____| + | | +LL | | (); + | |______- call expression requires function + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0618`. diff --git a/tests/ui/traits/new-solver/runaway-impl-candidate-selection.rs b/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.rs similarity index 100% rename from tests/ui/traits/new-solver/runaway-impl-candidate-selection.rs rename to tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.rs diff --git a/tests/ui/traits/new-solver/runaway-impl-candidate-selection.stderr b/tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr similarity index 100% rename from tests/ui/traits/new-solver/runaway-impl-candidate-selection.stderr rename to tests/ui/traits/new-solver/assembly/runaway-impl-candidate-selection.stderr diff --git a/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs similarity index 100% rename from tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.rs rename to tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.rs diff --git a/tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr b/tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr similarity index 100% rename from tests/ui/traits/new-solver/coinduction/fixpoint-exponential-growth.stderr rename to tests/ui/traits/new-solver/cycles/coinduction/fixpoint-exponential-growth.stderr diff --git a/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs b/tests/ui/traits/new-solver/cycles/coinduction/incompleteness-unstable-result.rs similarity index 100% rename from tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.rs rename to tests/ui/traits/new-solver/cycles/coinduction/incompleteness-unstable-result.rs diff --git a/tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr b/tests/ui/traits/new-solver/cycles/coinduction/incompleteness-unstable-result.stderr similarity index 100% rename from tests/ui/traits/new-solver/coinduction/incompleteness-unstable-result.stderr rename to tests/ui/traits/new-solver/cycles/coinduction/incompleteness-unstable-result.stderr diff --git a/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.rs b/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.rs new file mode 100644 index 000000000000..5617e45adf6a --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.rs @@ -0,0 +1,37 @@ +// compile-flags: -Ztrait-solver=next +#![feature(rustc_attrs)] + +// Test that having both an inductive and a coinductive cycle +// is handled correctly. + +#[rustc_coinductive] +trait Trait {} +impl Trait for T {} + +trait Inductive {} +impl Inductive for T {} +#[rustc_coinductive] +trait Coinductive {} +impl Coinductive for T {} + +fn impls_trait() {} + +#[rustc_coinductive] +trait TraitRev {} +impl TraitRev for T {} + +trait InductiveRev {} +impl InductiveRev for T {} +#[rustc_coinductive] +trait CoinductiveRev {} +impl CoinductiveRev for T {} + +fn impls_trait_rev() {} + +fn main() { + impls_trait::<()>(); + //~^ ERROR overflow evaluating the requirement + + impls_trait_rev::<()>(); + //~^ ERROR overflow evaluating the requirement +} diff --git a/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.stderr b/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.stderr new file mode 100644 index 000000000000..4b8846da535f --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/double-cycle-inductive-coinductive.stderr @@ -0,0 +1,29 @@ +error[E0275]: overflow evaluating the requirement `(): Trait` + --> $DIR/double-cycle-inductive-coinductive.rs:32:5 + | +LL | impls_trait::<()>(); + | ^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`) +note: required by a bound in `impls_trait` + --> $DIR/double-cycle-inductive-coinductive.rs:17:19 + | +LL | fn impls_trait() {} + | ^^^^^ required by this bound in `impls_trait` + +error[E0275]: overflow evaluating the requirement `(): TraitRev` + --> $DIR/double-cycle-inductive-coinductive.rs:35:5 + | +LL | impls_trait_rev::<()>(); + | ^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`double_cycle_inductive_coinductive`) +note: required by a bound in `impls_trait_rev` + --> $DIR/double-cycle-inductive-coinductive.rs:29:23 + | +LL | fn impls_trait_rev() {} + | ^^^^^^^^ required by this bound in `impls_trait_rev` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.rs b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.rs new file mode 100644 index 000000000000..cda98789886c --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.rs @@ -0,0 +1,48 @@ +// compile-flags: -Ztrait-solver=next +#![feature(trivial_bounds, marker_trait_attr)] +#![allow(trivial_bounds)] +// This previously triggered a bug in the provisional cache. +// +// This has the proof tree +// - `MultipleCandidates: Trait` proven via impl-one +// - `MultipleNested: Trait` via impl +// - `MultipleCandidates: Trait` (inductive cycle ~> OVERFLOW) +// - `DoesNotImpl: Trait` (ERR) +// - `MultipleCandidates: Trait` proven via impl-two +// - `MultipleNested: Trait` (in provisional cache ~> OVERFLOW) +// +// We previously incorrectly treated the `MultipleCandidates: Trait` as +// overflow because it was in the cache and reached via an inductive cycle. +// It should be `NoSolution`. + +struct MultipleCandidates; +struct MultipleNested; +struct DoesNotImpl; + +#[marker] +trait Trait {} + +// impl-one +impl Trait for MultipleCandidates +where + MultipleNested: Trait +{} + +// impl-two +impl Trait for MultipleCandidates +where + MultipleNested: Trait, +{} + +impl Trait for MultipleNested +where + MultipleCandidates: Trait, + DoesNotImpl: Trait, +{} + +fn impls_trait() {} + +fn main() { + impls_trait::(); + //~^ ERROR the trait bound `MultipleCandidates: Trait` is not satisfied +} diff --git a/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.stderr b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.stderr new file mode 100644 index 000000000000..57227321a6db --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-err.stderr @@ -0,0 +1,16 @@ +error[E0277]: the trait bound `MultipleCandidates: Trait` is not satisfied + --> $DIR/inductive-cycle-but-err.rs:46:19 + | +LL | impls_trait::(); + | ^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for `MultipleCandidates` + | + = help: the trait `Trait` is implemented for `MultipleCandidates` +note: required by a bound in `impls_trait` + --> $DIR/inductive-cycle-but-err.rs:43:19 + | +LL | fn impls_trait() {} + | ^^^^^ required by this bound in `impls_trait` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/new-solver/cycles/inductive-cycle-but-ok.rs b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-ok.rs new file mode 100644 index 000000000000..d4851eb694be --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/inductive-cycle-but-ok.rs @@ -0,0 +1,44 @@ +// compile-flags: -Ztrait-solver=next +// check-pass +#![feature(trivial_bounds, marker_trait_attr)] +#![allow(trivial_bounds)] + +// This previously triggered a bug in the provisional cache. +// +// This has the proof tree +// - `Root: Trait` proven via impl +// - `MultipleCandidates: Trait` +// - candidate: overflow-impl +// - `Root: Trait` (inductive cycle ~> OVERFLOW) +// - candidate: trivial-impl ~> YES +// - merge respones ~> YES +// - `MultipleCandidates: Trait` (in provisional cache ~> OVERFLOW) +// +// We previously incorrectly treated the `MultipleCandidates: Trait` as +// overflow because it was in the cache and reached via an inductive cycle. +// It should be `YES`. + +struct Root; +struct MultipleCandidates; + +#[marker] +trait Trait {} +impl Trait for Root +where + MultipleCandidates: Trait, + MultipleCandidates: Trait, +{} + +// overflow-impl +impl Trait for MultipleCandidates +where + Root: Trait, +{} +// trivial-impl +impl Trait for MultipleCandidates {} + +fn impls_trait() {} + +fn main() { + impls_trait::(); +} diff --git a/tests/ui/traits/new-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs b/tests/ui/traits/new-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs new file mode 100644 index 000000000000..530e6d0ecf32 --- /dev/null +++ b/tests/ui/traits/new-solver/cycles/inductive-cycle-discarded-coinductive-constraints.rs @@ -0,0 +1,36 @@ +// check-pass +// compile-flags: -Ztrait-solver=next +#![feature(rustc_attrs, marker_trait_attr)] +#[rustc_coinductive] +trait Trait {} + +impl Trait for (T, U) +where + (U, T): Trait, + (T, U): Inductive, + (): ConstrainToU32, +{} + +trait ConstrainToU32 {} +impl ConstrainToU32 for () {} + +// We only prefer the candidate without an inductive cycle +// once the inductive cycle has the same constraints as the +// other goal. +#[marker] +trait Inductive {} +impl Inductive for (T, U) +where + (T, U): Trait, +{} + +impl Inductive for (u32, u32) {} + +fn impls_trait() +where + (T, U): Trait, +{} + +fn main() { + impls_trait::<_, _>(); +} diff --git a/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs b/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs index f06b98a79cfe..3cfe7ab87f60 100644 --- a/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs +++ b/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.rs @@ -39,7 +39,7 @@ fn impls_ar() {} fn main() { impls_a::<()>(); - //~^ ERROR overflow evaluating the requirement `(): A` + // FIXME(-Ztrait-solver=next): This is broken and should error. impls_ar::<()>(); //~^ ERROR overflow evaluating the requirement `(): AR` diff --git a/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr b/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr index 33fac603cbdd..0e1c86c1bb34 100644 --- a/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr +++ b/tests/ui/traits/new-solver/cycles/inductive-not-on-stack.stderr @@ -1,16 +1,3 @@ -error[E0275]: overflow evaluating the requirement `(): A` - --> $DIR/inductive-not-on-stack.rs:41:5 - | -LL | impls_a::<()>(); - | ^^^^^^^^^^^^^ - | - = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`inductive_not_on_stack`) -note: required by a bound in `impls_a` - --> $DIR/inductive-not-on-stack.rs:25:15 - | -LL | fn impls_a() {} - | ^ required by this bound in `impls_a` - error[E0275]: overflow evaluating the requirement `(): AR` --> $DIR/inductive-not-on-stack.rs:44:5 | @@ -24,6 +11,6 @@ note: required by a bound in `impls_ar` LL | fn impls_ar() {} | ^^ required by this bound in `impls_ar` -error: aborting due to 2 previous errors +error: aborting due to previous error For more information about this error, try `rustc --explain E0275`. diff --git a/tests/ui/traits/new-solver/leak-check-coinductive-cycle.rs b/tests/ui/traits/new-solver/cycles/leak-check-coinductive-cycle.rs similarity index 100% rename from tests/ui/traits/new-solver/leak-check-coinductive-cycle.rs rename to tests/ui/traits/new-solver/cycles/leak-check-coinductive-cycle.rs index 1f7d4a49c901..a6d318726730 100644 --- a/tests/ui/traits/new-solver/leak-check-coinductive-cycle.rs +++ b/tests/ui/traits/new-solver/cycles/leak-check-coinductive-cycle.rs @@ -1,5 +1,5 @@ -// check-pass // compile-flags: -Ztrait-solver=next +// check-pass #![feature(rustc_attrs)] #[rustc_coinductive] diff --git a/tests/ui/traits/new-solver/provisional-result-done.rs b/tests/ui/traits/new-solver/cycles/provisional-result-done.rs similarity index 100% rename from tests/ui/traits/new-solver/provisional-result-done.rs rename to tests/ui/traits/new-solver/cycles/provisional-result-done.rs diff --git a/tests/ui/traits/new-solver/dyn-any-dont-prefer-impl.rs b/tests/ui/traits/new-solver/dyn-any-dont-prefer-impl.rs index 7d15b8c63925..af35a6195e00 100644 --- a/tests/ui/traits/new-solver/dyn-any-dont-prefer-impl.rs +++ b/tests/ui/traits/new-solver/dyn-any-dont-prefer-impl.rs @@ -1,6 +1,10 @@ // compile-flags: -Ztrait-solver=next // check-pass +// Test that selection prefers the builtin trait object impl for `Any` +// instead of the user defined impl. Both impls apply to the trait +// object. + use std::any::Any; fn needs_usize(_: &usize) {} diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.rs b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs similarity index 100% rename from tests/ui/traits/new-solver/exponential-trait-goals.rs rename to tests/ui/traits/new-solver/overflow/exponential-trait-goals.rs diff --git a/tests/ui/traits/new-solver/exponential-trait-goals.stderr b/tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr similarity index 100% rename from tests/ui/traits/new-solver/exponential-trait-goals.stderr rename to tests/ui/traits/new-solver/overflow/exponential-trait-goals.stderr diff --git a/tests/ui/traits/new-solver/recursive-self-normalization-2.rs b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs similarity index 100% rename from tests/ui/traits/new-solver/recursive-self-normalization-2.rs rename to tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.rs diff --git a/tests/ui/traits/new-solver/recursive-self-normalization-2.stderr b/tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr similarity index 100% rename from tests/ui/traits/new-solver/recursive-self-normalization-2.stderr rename to tests/ui/traits/new-solver/overflow/recursive-self-normalization-2.stderr diff --git a/tests/ui/traits/new-solver/recursive-self-normalization.rs b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs similarity index 100% rename from tests/ui/traits/new-solver/recursive-self-normalization.rs rename to tests/ui/traits/new-solver/overflow/recursive-self-normalization.rs diff --git a/tests/ui/traits/new-solver/recursive-self-normalization.stderr b/tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr similarity index 100% rename from tests/ui/traits/new-solver/recursive-self-normalization.stderr rename to tests/ui/traits/new-solver/overflow/recursive-self-normalization.stderr diff --git a/tests/ui/where-clauses/where-clause-placement-type-alias.stderr b/tests/ui/where-clauses/where-clause-placement-type-alias.stderr index b3c155a48ddb..d341148b04cb 100644 --- a/tests/ui/where-clauses/where-clause-placement-type-alias.stderr +++ b/tests/ui/where-clauses/where-clause-placement-type-alias.stderr @@ -4,7 +4,8 @@ error: where clauses are not allowed after the type for type aliases LL | type Bar = () where u32: Copy; | ^^^^^^^^^^^^^^^ | - = note: see issue #89122 for more information + = note: see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable error: where clauses are not allowed after the type for type aliases --> $DIR/where-clause-placement-type-alias.rs:8:15 @@ -12,7 +13,8 @@ error: where clauses are not allowed after the type for type aliases LL | type Baz = () where; | ^^^^^ | - = note: see issue #89122 for more information + = note: see issue #112792 for more information + = help: add `#![feature(lazy_type_alias)]` to the crate attributes to enable error: aborting due to 2 previous errors diff --git a/triagebot.toml b/triagebot.toml index 3b2114855c14..c513dce3f404 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -490,7 +490,7 @@ cc = ["@nnethercote"] [assign] warn_non_default_branch = true contributing_url = "https://rustc-dev-guide.rust-lang.org/getting-started.html" -users_on_vacation = ["jyn514", "WaffleLapkin"] +users_on_vacation = ["jyn514", "WaffleLapkin", "clubby789"] [assign.adhoc_groups] compiler-team = [ @@ -527,7 +527,6 @@ bootstrap = [ ] infra-ci = [ "@Mark-Simulacrum", - "@pietroalbini", ] rustdoc = [ "@jsha",