diff --git a/Cargo.lock b/Cargo.lock index 47ad7ab3e981..5e9808c80688 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -524,7 +524,7 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "clippy" -version = "0.1.82" +version = "0.1.83" dependencies = [ "anstream", "cargo_metadata 0.18.1", @@ -547,13 +547,13 @@ dependencies = [ "termize", "tokio", "toml 0.7.8", - "ui_test 0.25.0", + "ui_test", "walkdir", ] [[package]] name = "clippy_config" -version = "0.1.82" +version = "0.1.83" dependencies = [ "itertools", "serde", @@ -576,7 +576,7 @@ dependencies = [ [[package]] name = "clippy_lints" -version = "0.1.82" +version = "0.1.83" dependencies = [ "arrayvec", "cargo_metadata 0.18.1", @@ -600,7 +600,7 @@ dependencies = [ [[package]] name = "clippy_utils" -version = "0.1.82" +version = "0.1.83" dependencies = [ "arrayvec", "clippy_config", @@ -902,7 +902,7 @@ dependencies = [ [[package]] name = "declare_clippy_lint" -version = "0.1.82" +version = "0.1.83" dependencies = [ "itertools", "quote", @@ -2269,7 +2269,7 @@ dependencies = [ "rustc_version", "smallvec", "tempfile", - "ui_test 0.26.5", + "ui_test", "windows-sys 0.52.0", ] @@ -5485,33 +5485,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" -[[package]] -name = "ui_test" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e4f339f62edc873975c47115f9e71c5454ddaa37c1142b42fc0b2672c8dacb" -dependencies = [ - "annotate-snippets 0.11.4", - "anyhow", - "bstr", - "cargo-platform", - "cargo_metadata 0.18.1", - "color-eyre", - "colored", - "comma", - "crossbeam-channel", - "indicatif", - "lazy_static", - "levenshtein", - "prettydiff", - "regex", - "rustc_version", - "rustfix", - "serde", - "serde_json", - "spanned", -] - [[package]] name = "ui_test" version = "0.26.5" diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 614a99a6e196..83931a8c1a96 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -4,9 +4,9 @@ use rustc_ast::{NodeId, PatKind, attr, token}; use rustc_feature::{AttributeGate, BUILTIN_ATTRIBUTE_MAP, BuiltinAttribute, Features, GateIssue}; use rustc_session::Session; use rustc_session::parse::{feature_err, feature_err_issue, feature_warn}; -use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_span::symbol::sym; +use rustc_span::{Span, Symbol}; use rustc_target::spec::abi; use thin_vec::ThinVec; @@ -483,6 +483,8 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { maybe_stage_features(sess, features, krate); check_incompatible_features(sess, features); + check_new_solver_banned_features(sess, features); + let mut visitor = PostExpansionVisitor { sess, features }; let spans = sess.psess.gated_spans.spans.borrow(); @@ -662,3 +664,22 @@ fn check_incompatible_features(sess: &Session, features: &Features) { } } } + +fn check_new_solver_banned_features(sess: &Session, features: &Features) { + if !sess.opts.unstable_opts.next_solver.is_some_and(|n| n.globally) { + return; + } + + // Ban GCE with the new solver, because it does not implement GCE correctly. + if let Some(&(_, gce_span, _)) = features + .declared_lang_features + .iter() + .find(|&&(feat, _, _)| feat == sym::generic_const_exprs) + { + sess.dcx().emit_err(errors::IncompatibleFeatures { + spans: vec![gce_span], + f1: Symbol::intern("-Znext-solver=globally"), + f2: sym::generic_const_exprs, + }); + } +} diff --git a/compiler/rustc_borrowck/src/facts.rs b/compiler/rustc_borrowck/src/facts.rs index 713452796c60..ef8e757a5478 100644 --- a/compiler/rustc_borrowck/src/facts.rs +++ b/compiler/rustc_borrowck/src/facts.rs @@ -1,7 +1,7 @@ use std::error::Error; use std::fmt::Debug; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use std::path::Path; use polonius_engine::{AllFacts as PoloniusFacts, Atom}; @@ -127,7 +127,7 @@ impl<'w> FactWriter<'w> { T: FactRow, { let file = &self.dir.join(file_name); - let mut file = BufWriter::new(File::create(file)?); + let mut file = File::create_buffered(file)?; for row in rows { row.write(&mut file, self.location_table)?; } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 733fabe557e6..a11eca0b9c74 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -6,6 +6,7 @@ #![feature(assert_matches)] #![feature(box_patterns)] #![feature(control_flow_enum)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(never_type)] #![feature(rustc_attrs)] diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 8cfe93b4d9c9..339628053a95 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,22 @@ pub(crate) fn unsized_info<'tcx>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); return old_info; } diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 5e510177818b..1f7a923dd2c6 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -808,8 +808,7 @@ struct ThinLTOKeysMap { impl ThinLTOKeysMap { fn save_to_file(&self, path: &Path) -> io::Result<()> { use std::io::Write; - let file = File::create(path)?; - let mut writer = io::BufWriter::new(file); + let mut writer = File::create_buffered(path)?; // The entries are loaded back into a hash map in `load_from_file()`, so // the order in which we write them to file here does not matter. for (module, key) in &self.keys { @@ -821,8 +820,8 @@ impl ThinLTOKeysMap { fn load_from_file(path: &Path) -> io::Result { use std::io::BufRead; let mut keys = BTreeMap::default(); - let file = File::open(path)?; - for line in io::BufReader::new(file).lines() { + let file = File::open_buffered(path)?; + for line in file.lines() { let line = line?; let mut split = line.split(' '); let module = split.next().unwrap(); diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index d69112612ba6..bdfc0f626f80 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -11,6 +11,7 @@ #![feature(assert_matches)] #![feature(exact_size_is_empty)] #![feature(extern_types)] +#![feature(file_buffered)] #![feature(hash_raw_entry)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 892dfb91201e..69693230ce07 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1087,16 +1087,17 @@ fn link_natively( let strip = sess.opts.cg.strip; if sess.target.is_like_osx { + let stripcmd = "/usr/bin/strip"; match (strip, crate_type) { (Strip::Debuginfo, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-S")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S")) } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-x")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-x")) } (Strip::Symbols, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, None) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, None) } (Strip::None, _) => {} } diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 84817e198445..a73ec83ee62c 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1,9 +1,8 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; -use std::io::{self, BufWriter}; use std::path::{Path, PathBuf}; -use std::{env, iter, mem, str}; +use std::{env, io, iter, mem, str}; use cc::windows_registry; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; @@ -754,7 +753,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.target.is_like_osx { // Write a plain, newline-separated list of symbols let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; for sym in symbols { debug!(" _{sym}"); writeln!(f, "_{sym}")?; @@ -765,7 +764,7 @@ impl<'a> Linker for GccLinker<'a> { } } else if is_windows { let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // .def file similar to MSVC one but without LIBRARY section // because LD doesn't like when it's empty @@ -781,7 +780,7 @@ impl<'a> Linker for GccLinker<'a> { } else { // Write an LD version script let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; if !symbols.is_empty() { writeln!(f, " global:")?; @@ -1059,7 +1058,7 @@ impl<'a> Linker for MsvcLinker<'a> { let path = tmpdir.join("lib.def"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // Start off with the standard module name header and then go // straight to exports. @@ -1648,7 +1647,7 @@ impl<'a> Linker for AixLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("list.exp"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. for symbol in symbols { debug!(" _{symbol}"); @@ -1961,7 +1960,7 @@ impl<'a> Linker for BpfLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, _crate_type: CrateType, symbols: &[String]) { let path = tmpdir.join("symbols"); let res: io::Result<()> = try { - let mut f = BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; for sym in symbols { writeln!(f, "{sym}")?; } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 8e4385bee1aa..5c67600e4eec 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -37,6 +37,7 @@ use crate::back::write::{ submit_codegened_module_to_llvm, submit_post_lto_module_to_llvm, submit_pre_lto_module_to_llvm, }; use crate::common::{self, IntPredicate, RealPredicate, TypeKind}; +use crate::meth::load_vtable; use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; @@ -124,8 +125,28 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with - // invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); + + // A NOP cast that doesn't actually change anything, let's avoid any + // unnecessary work. This relies on the assumption that if the principal + // traits are equal, then the associated type bounds (`dyn Trait`) + // are also equal, which is ensured by the fact that normalization is + // a function and we do not allow overlapping impls. return old_info; } @@ -135,14 +156,8 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( if let Some(entry_idx) = vptr_entry_idx { let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = u64::try_from(entry_idx).unwrap() * ptr_size.bytes(); - let gep = bx.inbounds_ptradd(old_info, bx.const_usize(vtable_byte_offset)); - let new_vptr = bx.load(bx.type_ptr(), gep, ptr_align); - bx.nonnull_metadata(new_vptr); - // VTable loads are invariant. - bx.set_invariant_load(new_vptr); - new_vptr + load_vtable(bx, old_info, bx.type_ptr(), vtable_byte_offset, source, true) } else { old_info } diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index ec0520fbb09d..162d14272a55 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -6,6 +6,7 @@ #![doc(rust_logo)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] #![feature(negative_impls)] diff --git a/compiler/rustc_codegen_ssa/src/meth.rs b/compiler/rustc_codegen_ssa/src/meth.rs index ecc3b2b24f1c..7eb0ecd12ffe 100644 --- a/compiler/rustc_codegen_ssa/src/meth.rs +++ b/compiler/rustc_codegen_ssa/src/meth.rs @@ -28,27 +28,9 @@ impl<'a, 'tcx> VirtualIndex { let llty = bx.fn_ptr_backend_type(fn_abi); let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = self.0 * ptr_size.bytes(); - if bx.cx().sess().opts.unstable_opts.virtual_function_elimination - && bx.cx().sess().lto() == Lto::Fat - { - let typeid = bx - .typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty))) - .unwrap(); - let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); - func - } else { - let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); - let ptr = bx.load(llty, gep, ptr_align); - // VTable loads are invariant. - bx.set_invariant_load(ptr); - if nonnull { - bx.nonnull_metadata(ptr); - } - ptr - } + load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, nonnull) } pub(crate) fn get_optional_fn>( @@ -75,31 +57,27 @@ impl<'a, 'tcx> VirtualIndex { self, bx: &mut Bx, llvtable: Bx::Value, + ty: Ty<'tcx>, ) -> Bx::Value { // Load the data pointer from the object. debug!("get_int({:?}, {:?})", llvtable, self); let llty = bx.type_isize(); let ptr_size = bx.data_layout().pointer_size; - let ptr_align = bx.data_layout().pointer_align.abi; let vtable_byte_offset = self.0 * ptr_size.bytes(); - let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); - let ptr = bx.load(llty, gep, ptr_align); - // VTable loads are invariant. - bx.set_invariant_load(ptr); - ptr + load_vtable(bx, llvtable, llty, vtable_byte_offset, ty, false) } } /// This takes a valid `self` receiver type and extracts the principal trait -/// ref of the type. -fn expect_dyn_trait_in_self(ty: Ty<'_>) -> ty::PolyExistentialTraitRef<'_> { +/// ref of the type. Return `None` if there is no principal trait. +fn dyn_trait_in_self(ty: Ty<'_>) -> Option> { for arg in ty.peel_refs().walk() { if let GenericArgKind::Type(ty) = arg.unpack() && let ty::Dynamic(data, _, _) = ty.kind() { - return data.principal().expect("expected principal trait object"); + return data.principal(); } } @@ -138,3 +116,36 @@ pub(crate) fn get_vtable<'tcx, Cx: CodegenMethods<'tcx>>( cx.vtables().borrow_mut().insert((ty, trait_ref), vtable); vtable } + +/// Call this function whenever you need to load a vtable. +pub(crate) fn load_vtable<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( + bx: &mut Bx, + llvtable: Bx::Value, + llty: Bx::Type, + vtable_byte_offset: u64, + ty: Ty<'tcx>, + nonnull: bool, +) -> Bx::Value { + let ptr_align = bx.data_layout().pointer_align.abi; + + if bx.cx().sess().opts.unstable_opts.virtual_function_elimination + && bx.cx().sess().lto() == Lto::Fat + { + if let Some(trait_ref) = dyn_trait_in_self(ty) { + let typeid = bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), trait_ref)).unwrap(); + let func = bx.type_checked_load(llvtable, vtable_byte_offset, typeid); + return func; + } else if nonnull { + bug!("load nonnull value from a vtable without a principal trait") + } + } + + let gep = bx.inbounds_ptradd(llvtable, bx.const_usize(vtable_byte_offset)); + let ptr = bx.load(llty, gep, ptr_align); + // VTable loads are invariant. + bx.set_invariant_load(ptr); + if nonnull { + bx.nonnull_metadata(ptr); + } + ptr +} diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 9874809ea2bb..146f55f95c21 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -1,4 +1,5 @@ use std::collections::hash_map::Entry; +use std::marker::PhantomData; use std::ops::Range; use rustc_data_structures::fx::FxHashMap; @@ -14,7 +15,7 @@ use rustc_target::abi::{Abi, FieldIdx, FieldsShape, Size, VariantIdx}; use super::operand::{OperandRef, OperandValue}; use super::place::{PlaceRef, PlaceValue}; -use super::{FunctionCx, LocalRef}; +use super::{FunctionCx, LocalRef, PerLocalVarDebugInfoIndexVec}; use crate::traits::*; pub struct FunctionDebugContext<'tcx, S, L> { @@ -48,6 +49,17 @@ pub struct PerLocalVarDebugInfo<'tcx, D> { pub projection: &'tcx ty::List>, } +/// Information needed to emit a constant. +pub struct ConstDebugInfo<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { + pub name: String, + pub source_info: mir::SourceInfo, + pub operand: OperandRef<'tcx, Bx::Value>, + pub dbg_var: Bx::DIVariable, + pub dbg_loc: Bx::DILocation, + pub fragment: Option>, + pub _phantom: PhantomData<&'a ()>, +} + #[derive(Clone, Copy, Debug)] pub struct DebugScope { pub dbg_scope: S, @@ -427,11 +439,25 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - pub(crate) fn debug_introduce_locals(&self, bx: &mut Bx) { + pub(crate) fn debug_introduce_locals( + &self, + bx: &mut Bx, + consts: Vec>, + ) { if bx.sess().opts.debuginfo == DebugInfo::Full || !bx.sess().fewer_names() { for local in self.locals.indices() { self.debug_introduce_local(bx, local); } + + for ConstDebugInfo { name, source_info, operand, dbg_var, dbg_loc, fragment, .. } in + consts.into_iter() + { + self.set_debug_loc(bx, source_info); + let base = FunctionCx::spill_operand_to_stack(operand, Some(name), bx); + bx.clear_dbg_loc(); + + bx.dbg_var_addr(dbg_var, dbg_loc, base.val.llval, Size::ZERO, &[], fragment); + } } } @@ -439,7 +465,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { pub(crate) fn compute_per_local_var_debug_info( &self, bx: &mut Bx, - ) -> Option>>> { + ) -> Option<( + PerLocalVarDebugInfoIndexVec<'tcx, Bx::DIVariable>, + Vec>, + )> { let full_debug_info = self.cx.sess().opts.debuginfo == DebugInfo::Full; let target_is_msvc = self.cx.sess().target.is_like_msvc; @@ -449,6 +478,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } let mut per_local = IndexVec::from_elem(vec![], &self.mir.local_decls); + let mut constants = vec![]; let mut params_seen: FxHashMap<_, Bx::DIVariable> = Default::default(); for var in &self.mir.var_debug_info { let dbg_scope_and_span = if full_debug_info { @@ -545,23 +575,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let Some(dbg_loc) = self.dbg_loc(var.source_info) else { continue }; let operand = self.eval_mir_constant_to_operand(bx, &c); - self.set_debug_loc(bx, var.source_info); - let base = - Self::spill_operand_to_stack(operand, Some(var.name.to_string()), bx); - bx.clear_dbg_loc(); - - bx.dbg_var_addr( + constants.push(ConstDebugInfo { + name: var.name.to_string(), + source_info: var.source_info, + operand, dbg_var, dbg_loc, - base.val.llval, - Size::ZERO, - &[], fragment, - ); + _phantom: PhantomData, + }); } } } } - Some(per_local) + Some((per_local, constants)) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index 71541e6ab41a..32cc78187b9e 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -126,7 +126,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { sym::vtable_align => ty::COMMON_VTABLE_ENTRIES_ALIGN, _ => bug!(), }; - let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable); + let value = meth::VirtualIndex::from_index(idx).get_usize(bx, vtable, callee_ty); match name { // Size is always <= isize::MAX. sym::vtable_size => { diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 202baddec723..c4fb24aa625d 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -41,6 +41,9 @@ enum CachedLlbb { Skip, } +type PerLocalVarDebugInfoIndexVec<'tcx, V> = + IndexVec>>; + /// Master context for codegenning from MIR. pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { instance: Instance<'tcx>, @@ -107,8 +110,7 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> { /// All `VarDebugInfo` from the MIR body, partitioned by `Local`. /// This is `None` if no variable debuginfo/names are needed. - per_local_var_debug_info: - Option>>>, + per_local_var_debug_info: Option>, /// Caller location propagated if this function has `#[track_caller]`. caller_location: Option>, @@ -216,7 +218,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // monomorphization, and if there is an error during collection then codegen never starts -- so // we don't have to do it again. - fx.per_local_var_debug_info = fx.compute_per_local_var_debug_info(&mut start_bx); + let (per_local_var_debug_info, consts_debug_info) = + fx.compute_per_local_var_debug_info(&mut start_bx).unzip(); + fx.per_local_var_debug_info = per_local_var_debug_info; let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance); let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order); @@ -268,7 +272,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( fx.initialize_locals(local_values); // Apply debuginfo to the newly allocated locals. - fx.debug_introduce_locals(&mut start_bx); + fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default()); // If the backend supports coverage, and coverage is enabled for this function, // do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps). diff --git a/compiler/rustc_codegen_ssa/src/size_of_val.rs b/compiler/rustc_codegen_ssa/src/size_of_val.rs index 3f3ae21035d5..827b939217e8 100644 --- a/compiler/rustc_codegen_ssa/src/size_of_val.rs +++ b/compiler/rustc_codegen_ssa/src/size_of_val.rs @@ -28,9 +28,9 @@ pub fn size_and_align_of_dst<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( // Load size/align from vtable. let vtable = info.unwrap(); let size = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_SIZE) - .get_usize(bx, vtable); + .get_usize(bx, vtable, t); let align = meth::VirtualIndex::from_index(ty::COMMON_VTABLE_ENTRIES_ALIGN) - .get_usize(bx, vtable); + .get_usize(bx, vtable, t); // Size is always <= isize::MAX. let size_bound = bx.data_layout().ptr_sized_integer().signed_max() as u128; diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index a35f5b1f17db..f225684d99ff 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -23,6 +23,7 @@ #![feature(cfg_match)] #![feature(core_intrinsics)] #![feature(extend_one)] +#![feature(file_buffered)] #![feature(hash_raw_entry)] #![feature(macro_metavar_expr)] #![feature(map_try_insert)] diff --git a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs index 60cde9a52b41..65a24366db83 100644 --- a/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs +++ b/compiler/rustc_data_structures/src/obligation_forest/graphviz.rs @@ -1,6 +1,5 @@ use std::env::var_os; use std::fs::File; -use std::io::BufWriter; use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; @@ -33,7 +32,7 @@ impl ObligationForest { let file_path = dir.as_ref().join(format!("{counter:010}_{description}.gv")); - let mut gv_file = BufWriter::new(File::create(file_path).unwrap()); + let mut gv_file = File::create_buffered(file_path).unwrap(); dot::render(&self, &mut gv_file).unwrap(); } diff --git a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs index c64741625a4e..7557219aaa69 100644 --- a/compiler/rustc_hir_analysis/src/collect/item_bounds.rs +++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs @@ -1,8 +1,9 @@ -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_hir as hir; use rustc_infer::traits::util; +use rustc_middle::ty::fold::shift_vars; use rustc_middle::ty::{ - self, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, + self, GenericArgs, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; use rustc_middle::{bug, span_bug}; use rustc_span::Span; @@ -42,14 +43,18 @@ fn associated_type_bounds<'tcx>( let trait_def_id = tcx.local_parent(assoc_item_def_id); let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id); - let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| { - match pred.kind().skip_binder() { - ty::ClauseKind::Trait(tr) => tr.self_ty() == item_ty, - ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty() == item_ty, - ty::ClauseKind::TypeOutlives(outlives) => outlives.0 == item_ty, - _ => false, - } - }); + let item_trait_ref = ty::TraitRef::identity(tcx, tcx.parent(assoc_item_def_id.to_def_id())); + let bounds_from_parent = + trait_predicates.predicates.iter().copied().filter_map(|(clause, span)| { + remap_gat_vars_and_recurse_into_nested_projections( + tcx, + filter, + item_trait_ref, + assoc_item_def_id, + span, + clause, + ) + }); let all_bounds = tcx.arena.alloc_from_iter(bounds.clauses(tcx).chain(bounds_from_parent)); debug!( @@ -63,6 +68,226 @@ fn associated_type_bounds<'tcx>( all_bounds } +/// The code below is quite involved, so let me explain. +/// +/// We loop here, because we also want to collect vars for nested associated items as +/// well. For example, given a clause like `Self::A::B`, we want to add that to the +/// item bounds for `A`, so that we may use that bound in the case that `Self::A::B` is +/// rigid. +/// +/// Secondly, regarding bound vars, when we see a where clause that mentions a GAT +/// like `for<'a, ...> Self::Assoc<'a, ...>: Bound<'b, ...>`, we want to turn that into +/// an item bound on the GAT, where all of the GAT args are substituted with the GAT's +/// param regions, and then keep all of the other late-bound vars in the bound around. +/// We need to "compress" the binder so that it doesn't mention any of those vars that +/// were mapped to params. +fn remap_gat_vars_and_recurse_into_nested_projections<'tcx>( + tcx: TyCtxt<'tcx>, + filter: PredicateFilter, + item_trait_ref: ty::TraitRef<'tcx>, + assoc_item_def_id: LocalDefId, + span: Span, + clause: ty::Clause<'tcx>, +) -> Option<(ty::Clause<'tcx>, Span)> { + let mut clause_ty = match clause.kind().skip_binder() { + ty::ClauseKind::Trait(tr) => tr.self_ty(), + ty::ClauseKind::Projection(proj) => proj.projection_term.self_ty(), + ty::ClauseKind::TypeOutlives(outlives) => outlives.0, + _ => return None, + }; + + let gat_vars = loop { + if let ty::Alias(ty::Projection, alias_ty) = *clause_ty.kind() { + if alias_ty.trait_ref(tcx) == item_trait_ref + && alias_ty.def_id == assoc_item_def_id.to_def_id() + { + // We have found the GAT in question... + // Return the vars, since we may need to remap them. + break &alias_ty.args[item_trait_ref.args.len()..]; + } else { + // Only collect *self* type bounds if the filter is for self. + match filter { + PredicateFilter::SelfOnly | PredicateFilter::SelfThatDefines(_) => { + return None; + } + PredicateFilter::All | PredicateFilter::SelfAndAssociatedTypeBounds => {} + } + + clause_ty = alias_ty.self_ty(); + continue; + } + } + + return None; + }; + + // Special-case: No GAT vars, no mapping needed. + if gat_vars.is_empty() { + return Some((clause, span)); + } + + // First, check that all of the GAT args are substituted with a unique late-bound arg. + // If we find a duplicate, then it can't be mapped to the definition's params. + let mut mapping = FxIndexMap::default(); + let generics = tcx.generics_of(assoc_item_def_id); + for (param, var) in std::iter::zip(&generics.own_params, gat_vars) { + let existing = match var.unpack() { + ty::GenericArgKind::Lifetime(re) => { + if let ty::RegionKind::ReBound(ty::INNERMOST, bv) = re.kind() { + mapping.insert(bv.var, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + ty::GenericArgKind::Type(ty) => { + if let ty::Bound(ty::INNERMOST, bv) = *ty.kind() { + mapping.insert(bv.var, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + ty::GenericArgKind::Const(ct) => { + if let ty::ConstKind::Bound(ty::INNERMOST, bv) = ct.kind() { + mapping.insert(bv, tcx.mk_param_from_def(param)) + } else { + return None; + } + } + }; + + if existing.is_some() { + return None; + } + } + + // Finally, map all of the args in the GAT to the params we expect, and compress + // the remaining late-bound vars so that they count up from var 0. + let mut folder = + MapAndCompressBoundVars { tcx, binder: ty::INNERMOST, still_bound_vars: vec![], mapping }; + let pred = clause.kind().skip_binder().fold_with(&mut folder); + + Some(( + ty::Binder::bind_with_vars(pred, tcx.mk_bound_variable_kinds(&folder.still_bound_vars)) + .upcast(tcx), + span, + )) +} + +/// Given some where clause like `for<'b, 'c> >::Gat<'b>: Bound<'c>`, +/// the mapping will map `'b` back to the GAT's `'b_identity`. Then we need to compress the +/// remaining bound var `'c` to index 0. +/// +/// This folder gives us: `for<'c> >::Gat<'b_identity>: Bound<'c>`, +/// which is sufficient for an item bound for `Gat`, since all of the GAT's args are identity. +struct MapAndCompressBoundVars<'tcx> { + tcx: TyCtxt<'tcx>, + /// How deep are we? Makes sure we don't touch the vars of nested binders. + binder: ty::DebruijnIndex, + /// List of bound vars that remain unsubstituted because they were not + /// mentioned in the GAT's args. + still_bound_vars: Vec, + /// Subtle invariant: If the `GenericArg` is bound, then it should be + /// stored with the debruijn index of `INNERMOST` so it can be shifted + /// correctly during substitution. + mapping: FxIndexMap>, +} + +impl<'tcx> TypeFolder> for MapAndCompressBoundVars<'tcx> { + fn cx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + ty::Binder<'tcx, T>: TypeSuperFoldable>, + { + self.binder.shift_in(1); + let out = t.super_fold_with(self); + self.binder.shift_out(1); + out + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + if !ty.has_bound_vars() { + return ty; + } + + if let ty::Bound(binder, old_bound) = *ty.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_ty() + } else { + // If we didn't find a mapped generic, then make a new one. + // Allocate a new var idx, and insert a new bound ty. + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Ty(old_bound.kind)); + let mapped = Ty::new_bound(self.tcx, ty::INNERMOST, ty::BoundTy { + var, + kind: old_bound.kind, + }); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ty.super_fold_with(self) + } + } + + fn fold_region(&mut self, re: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReBound(binder, old_bound) = re.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_bound.var) { + mapped.expect_region() + } else { + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Region(old_bound.kind)); + let mapped = ty::Region::new_bound(self.tcx, ty::INNERMOST, ty::BoundRegion { + var, + kind: old_bound.kind, + }); + self.mapping.insert(old_bound.var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + re + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.has_bound_vars() { + return ct; + } + + if let ty::ConstKind::Bound(binder, old_var) = ct.kind() + && self.binder == binder + { + let mapped = if let Some(mapped) = self.mapping.get(&old_var) { + mapped.expect_const() + } else { + let var = ty::BoundVar::from_usize(self.still_bound_vars.len()); + self.still_bound_vars.push(ty::BoundVariableKind::Const); + let mapped = ty::Const::new_bound(self.tcx, ty::INNERMOST, var); + self.mapping.insert(old_var, mapped.into()); + mapped + }; + + shift_vars(self.tcx, mapped, self.binder.as_u32()) + } else { + ct.super_fold_with(self) + } + } + + fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> { + if !p.has_bound_vars() { p } else { p.super_fold_with(self) } + } +} + /// Opaque types don't inherit bounds from their parent: for return position /// impl trait it isn't possible to write a suitable predicate on the /// containing function and for type-alias impl trait we don't have a backwards diff --git a/compiler/rustc_incremental/src/assert_dep_graph.rs b/compiler/rustc_incremental/src/assert_dep_graph.rs index 646b9dbe1331..a006786aa75b 100644 --- a/compiler/rustc_incremental/src/assert_dep_graph.rs +++ b/compiler/rustc_incremental/src/assert_dep_graph.rs @@ -35,7 +35,7 @@ use std::env; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::graph::implementation::{Direction, INCOMING, NodeIndex, OUTGOING}; @@ -245,7 +245,7 @@ fn dump_graph(query: &DepGraphQuery) { { // dump a .txt file with just the edges: let txt_path = format!("{path}.txt"); - let mut file = BufWriter::new(File::create(&txt_path).unwrap()); + let mut file = File::create_buffered(&txt_path).unwrap(); for (source, target) in &edges { write!(file, "{source:?} -> {target:?}\n").unwrap(); } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index dda1232b80bf..e8735cba9bd2 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -5,6 +5,7 @@ #![deny(missing_docs)] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] +#![feature(file_buffered)] #![feature(rustdoc_internals)] #![warn(unreachable_pub)] // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index 94ebe51f213f..b81a74027016 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(decl_macro)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 617581cf667f..bba517915a2d 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -519,7 +519,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P write_deps_to_file(&mut file)?; } OutFileName::Real(ref path) => { - let mut file = BufWriter::new(fs::File::create(path)?); + let mut file = fs::File::create_buffered(path)?; write_deps_to_file(&mut file)?; } } diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index a58f3c6b5caa..4450d050c8e1 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -128,8 +128,7 @@ pub fn non_durable_rename(src: &Path, dst: &Path) -> std::io::Result<()> { } pub fn copy_to_stdout(from: &Path) -> io::Result<()> { - let file = fs::File::open(from)?; - let mut reader = io::BufReader::new(file); + let mut reader = fs::File::open_buffered(from)?; let mut stdout = io::stdout(); io::copy(&mut reader, &mut stdout)?; Ok(()) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 1759ea7d4415..10f2087d1e6f 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -8,6 +8,7 @@ #![feature(decl_macro)] #![feature(error_iter)] #![feature(extract_if)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(iter_from_coroutine)] #![feature(let_chains)] diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 23cd247e8841..a32b19b067a5 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -45,6 +45,7 @@ #![feature(discriminant_kind)] #![feature(extern_types)] #![feature(extract_if)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(intra_doc_pointers)] #![feature(iter_from_coroutine)] diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index 119aed1106dd..487895652183 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -277,9 +277,9 @@ pub fn create_dump_file<'tcx>( ) })?; } - Ok(io::BufWriter::new(fs::File::create(&file_path).map_err(|e| { + Ok(fs::File::create_buffered(&file_path).map_err(|e| { io::Error::new(e.kind(), format!("IO error creating MIR dump file: {file_path:?}; {e}")) - })?)) + })?) } /////////////////////////////////////////////////////////////////////////// diff --git a/compiler/rustc_mir_dataflow/src/framework/engine.rs b/compiler/rustc_mir_dataflow/src/framework/engine.rs index 947bdf860b58..da01a9740943 100644 --- a/compiler/rustc_mir_dataflow/src/framework/engine.rs +++ b/compiler/rustc_mir_dataflow/src/framework/engine.rs @@ -266,7 +266,7 @@ where A::Domain: DebugWithContext, { use std::fs; - use std::io::{self, Write}; + use std::io::Write; let def_id = body.source.def_id(); let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { @@ -281,8 +281,7 @@ where if let Some(parent) = path.parent() { fs::create_dir_all(parent)?; } - let f = fs::File::create(&path)?; - io::BufWriter::new(f) + fs::File::create_buffered(&path)? } None if dump_enabled(tcx, A::NAME, def_id) => { diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index cb7c7edc413a..cd926c076412 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -3,6 +3,7 @@ #![feature(associated_type_defaults)] #![feature(box_patterns)] #![feature(exact_size_is_empty)] +#![feature(file_buffered)] #![feature(let_chains)] #![feature(try_blocks)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_mir_transform/src/dump_mir.rs b/compiler/rustc_mir_transform/src/dump_mir.rs index d7c473604196..5dd84975b88e 100644 --- a/compiler/rustc_mir_transform/src/dump_mir.rs +++ b/compiler/rustc_mir_transform/src/dump_mir.rs @@ -24,7 +24,7 @@ pub fn emit_mir(tcx: TyCtxt<'_>) -> io::Result<()> { write_mir_pretty(tcx, None, &mut f)?; } OutFileName::Real(path) => { - let mut f = io::BufWriter::new(File::create(&path)?); + let mut f = File::create_buffered(&path)?; write_mir_pretty(tcx, None, &mut f)?; if tcx.sess.opts.json_artifact_notifications { tcx.dcx().emit_artifact_notification(&path, "mir"); diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 56268c1d201d..4c090665992b 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -3,6 +3,7 @@ #![feature(box_patterns)] #![feature(const_type_name)] #![feature(cow_is_borrowed)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(let_chains)] diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index eda0b8c75f32..e353be6a105f 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_infer::traits::Reveal; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause, Reveal}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -16,6 +17,8 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_target::abi::{FIRST_VARIANT, Size}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::Upcast; use crate::util::{is_within_packed, relate_types}; @@ -586,6 +589,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { crate::util::relate_types(self.tcx, self.param_env, variance, src, dest) } + + /// Check that the given predicate definitely holds in the param-env of this MIR body. + fn predicate_must_hold_modulo_regions( + &self, + pred: impl Upcast, ty::Predicate<'tcx>>, + ) -> bool { + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + pred, + )); + ocx.select_all_or_error().is_empty() + } } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { @@ -1202,8 +1221,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { - // This is used for all `CoerceUnsized` types, - // not just pointers/references, so is hard to check. + // Pointers being unsize coerced should at least implement + // `CoerceUnsized`. + if !self.predicate_must_hold_modulo_regions(ty::TraitRef::new( + self.tcx, + self.tcx.require_lang_item( + LangItem::CoerceUnsized, + Some(self.body.source_info(location).span), + ), + [op_ty, *target_type], + )) { + self.fail(location, format!("Unsize coercion, but `{op_ty}` isn't coercible to `{target_type}`")); + } } CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { // FIXME(dyn-star): make sure nothing needs to be done here. diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 91101ddd5902..e92e6978d0f4 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -1,5 +1,6 @@ // tidy-alphabetical-start #![feature(array_windows)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(let_chains)] #![warn(unreachable_pub)] diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 1c1e6164f2eb..ad05cca66cab 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -95,7 +95,7 @@ use std::cmp; use std::collections::hash_map::Entry; use std::fs::{self, File}; -use std::io::{BufWriter, Write}; +use std::io::Write; use std::path::{Path, PathBuf}; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; @@ -1243,8 +1243,7 @@ fn dump_mono_items_stats<'tcx>( let ext = format.extension(); let filename = format!("{crate_name}.mono_items.{ext}"); let output_path = output_directory.join(&filename); - let file = File::create(&output_path)?; - let mut file = BufWriter::new(file); + let mut file = File::create_buffered(&output_path)?; // Gather instantiated mono items grouped by def_id let mut items_per_def_id: FxIndexMap<_, Vec<_>> = Default::default(); diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs index 3f0b23a595c0..f327c1fd1790 100644 --- a/compiler/rustc_target/src/spec/mod.rs +++ b/compiler/rustc_target/src/spec/mod.rs @@ -1840,6 +1840,8 @@ supported_targets! { ("powerpc-wrs-vxworks", powerpc_wrs_vxworks), ("powerpc-wrs-vxworks-spe", powerpc_wrs_vxworks_spe), ("powerpc64-wrs-vxworks", powerpc64_wrs_vxworks), + ("riscv32-wrs-vxworks", riscv32_wrs_vxworks), + ("riscv64-wrs-vxworks", riscv64_wrs_vxworks), ("aarch64-kmc-solid_asp3", aarch64_kmc_solid_asp3), ("armv7a-kmc-solid_asp3-eabi", armv7a_kmc_solid_asp3_eabi), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs index 0942f5d896bb..9d5bce053500 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_linux_ohos.rs @@ -5,8 +5,7 @@ pub(crate) fn target() -> Target { base.max_atomic_width = Some(128); Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "aarch64-unknown-linux-musl".into(), + llvm_target: "aarch64-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("ARM64 OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs index b74c49f0a090..dd5298944e0d 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_wrs_vxworks.rs @@ -7,7 +7,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32".into(), diff --git a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs index 04ea9e3468d2..92b09bcc45c4 100644 --- a/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/armv7_unknown_linux_ohos.rs @@ -7,8 +7,7 @@ pub(crate) fn target() -> Target { // Most of these settings are copied from the armv7_unknown_linux_musleabi // target. Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "armv7-unknown-linux-gnueabi".into(), + llvm_target: "armv7-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("Armv7-A OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs index c2b9bc14ca43..dfffa6e138f8 100644 --- a/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/i686_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-\ diff --git a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs index f9f7098684e6..32a856be6690 100644 --- a/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/loongarch64_unknown_linux_ohos.rs @@ -2,8 +2,7 @@ use crate::spec::{Target, TargetOptions, base}; pub(crate) fn target() -> Target { Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "loongarch64-unknown-linux-musl".into(), + llvm_target: "loongarch64-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("LoongArch64 OpenHarmony".into()), tier: Some(3), diff --git a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs index 11cf957cc185..1d3d9f1b77df 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc64_wrs_vxworks.rs @@ -14,7 +14,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: "E-m:e-Fi64-i64:64-n32:64-S128-v256:256:256-v512:512:512".into(), diff --git a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs index 24cad76b0004..22b38208b108 100644 --- a/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/powerpc_wrs_vxworks.rs @@ -13,7 +13,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 32, data_layout: "E-m:e-p:32:32-Fn32-i64:64-n32".into(), diff --git a/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs new file mode 100644 index 000000000000..4d3df78a5636 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv32_wrs_vxworks.rs @@ -0,0 +1,24 @@ +use crate::spec::{StackProbeType, Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "riscv32".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 32, + data_layout: "e-m:e-p:32:32-i64:64-n32-S128".into(), + arch: "riscv32".into(), + options: TargetOptions { + cpu: "generic-rv32".into(), + llvm_abiname: "ilp32d".into(), + max_atomic_width: Some(32), + features: "+m,+a,+f,+d,+c".into(), + stack_probes: StackProbeType::Inline, + ..base::vxworks::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs new file mode 100644 index 000000000000..720549e6a017 --- /dev/null +++ b/compiler/rustc_target/src/spec/targets/riscv64_wrs_vxworks.rs @@ -0,0 +1,24 @@ +use crate::spec::{StackProbeType, Target, TargetOptions, base}; + +pub(crate) fn target() -> Target { + Target { + llvm_target: "riscv64".into(), + metadata: crate::spec::TargetMetadata { + description: None, + tier: Some(3), + host_tools: Some(false), + std: Some(true), + }, + pointer_width: 64, + data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(), + arch: "riscv64".into(), + options: TargetOptions { + cpu: "generic-rv64".into(), + llvm_abiname: "lp64d".into(), + max_atomic_width: Some(64), + features: "+m,+a,+f,+d,+c".into(), + stack_probes: StackProbeType::Inline, + ..base::vxworks::opts() + }, + } +} diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs index e455aad5f51a..522943c91a5e 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_ohos.rs @@ -15,8 +15,7 @@ pub(crate) fn target() -> Target { base.supports_xray = true; Target { - // LLVM 15 doesn't support OpenHarmony yet, use a linux target instead. - llvm_target: "x86_64-unknown-linux-musl".into(), + llvm_target: "x86_64-unknown-linux-ohos".into(), metadata: crate::spec::TargetMetadata { description: Some("x86_64 OpenHarmony".into()), tier: Some(2), diff --git a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs index 8fbdd8f57f65..f003f939ad11 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_wrs_vxworks.rs @@ -15,7 +15,7 @@ pub(crate) fn target() -> Target { description: None, tier: Some(3), host_tools: Some(false), - std: None, // ? + std: Some(true), }, pointer_width: 64, data_layout: diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs index e0f8d475a2e3..e2cdcb3dd511 100644 --- a/library/core/src/slice/sort/shared/mod.rs +++ b/library/core/src/slice/sort/shared/mod.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "optimize_for_size", allow(dead_code))] +#![cfg_attr(any(feature = "optimize_for_size", target_pointer_width = "16"), allow(dead_code))] use crate::marker::Freeze; diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index e13fbc37e80c..7adcc83b818d 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -1,22 +1,22 @@ //! This module contains the entry points for `slice::sort`. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::cmp; use crate::intrinsics; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::{ SMALL_SORT_GENERAL_SCRATCH_LEN, StableSmallSortTypeImpl, insertion_sort_shift_left, }; pub(crate) mod merge; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] pub(crate) mod drift; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] pub(crate) mod quicksort; -#[cfg(feature = "optimize_for_size")] +#[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] pub(crate) mod tiny; /// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. @@ -45,7 +45,7 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less cfg_if! { if #[cfg(target_pointer_width = "16")] { - let heap_buf = BufT::with_capacity(alloc_len); + let mut heap_buf = BufT::with_capacity(alloc_len); let scratch = heap_buf.as_uninit_slice_mut(); } else { // For small inputs 4KiB of stack storage suffices, which allows us to avoid @@ -85,7 +85,7 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { // By allocating n elements of memory we can ensure the entire input can diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 8bbd85443d47..2eb653c4601a 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -2,9 +2,9 @@ use crate::intrinsics; use crate::mem::SizedTypeProperties; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; @@ -55,7 +55,7 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn ipnsort(v: &mut [T], is_less: &mut F) where diff --git a/library/panic_unwind/src/lib.rs b/library/panic_unwind/src/lib.rs index 4552fb68d26d..6cd4dffb8aa3 100644 --- a/library/panic_unwind/src/lib.rs +++ b/library/panic_unwind/src/lib.rs @@ -48,7 +48,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems"))), + all(target_family = "unix", not(any(target_os = "espidf", target_os = "rtems", target_os = "nuttx"))), all(target_vendor = "fortanix", target_env = "sgx"), target_family = "wasm", ))] { diff --git a/library/std/build.rs b/library/std/build.rs index ba1eece46f3c..359ae4f20eea 100644 --- a/library/std/build.rs +++ b/library/std/build.rs @@ -54,6 +54,7 @@ fn main() { || target_os == "teeos" || target_os == "zkvm" || target_os == "rtems" + || target_os == "nuttx" // See src/bootstrap/src/core/build_steps/synthetic_targets.rs || env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok() diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 55f3b628ce8e..db7867337dd5 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -375,6 +375,44 @@ impl File { OpenOptions::new().read(true).open(path.as_ref()) } + /// Attempts to open a file in read-only mode with buffering. + /// + /// See the [`OpenOptions::open`] method, the [`BufReader`][io::BufReader] type, + /// and the [`BufRead`][io::BufRead] trait for more details. + /// + /// If you only need to read the entire file contents, + /// consider [`std::fs::read()`][self::read] or + /// [`std::fs::read_to_string()`][self::read_to_string] instead. + /// + /// # Errors + /// + /// This function will return an error if `path` does not already exist. + /// Other errors may also be returned according to [`OpenOptions::open`]. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::BufRead; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::open_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for (line, i) in f.lines().zip(1..) { + /// println!("{i:6}: {}", line?); + /// } + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn open_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufReader::::try_new_buffer()?; + let file = File::open(path)?; + Ok(io::BufReader::with_buffer(file, buffer)) + } + /// Opens a file in write-only mode. /// /// This function will create a file if it does not exist, @@ -404,6 +442,45 @@ impl File { OpenOptions::new().write(true).create(true).truncate(true).open(path.as_ref()) } + /// Opens a file in write-only mode with buffering. + /// + /// This function will create a file if it does not exist, + /// and will truncate it if it does. + /// + /// Depending on the platform, this function may fail if the + /// full directory path does not exist. + /// + /// See the [`OpenOptions::open`] method and the + /// [`BufWriter`][io::BufWriter] type for more details. + /// + /// See also [`std::fs::write()`][self::write] for a simple function to + /// create a file with some given data. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(file_buffered)] + /// use std::fs::File; + /// use std::io::Write; + /// + /// fn main() -> std::io::Result<()> { + /// let mut f = File::create_buffered("foo.txt")?; + /// assert!(f.capacity() > 0); + /// for i in 0..100 { + /// writeln!(&mut f, "{i}")?; + /// } + /// f.flush()?; + /// Ok(()) + /// } + /// ``` + #[unstable(feature = "file_buffered", issue = "130804")] + pub fn create_buffered>(path: P) -> io::Result> { + // Allocate the buffer *first* so we don't affect the filesystem otherwise. + let buffer = io::BufWriter::::try_new_buffer()?; + let file = File::create(path)?; + Ok(io::BufWriter::with_buffer(file, buffer)) + } + /// Creates a new file in read-write mode; error if the file exists. /// /// This function will create a file if it does not exist, or return an error if it does. This diff --git a/library/std/src/io/buffered/bufreader.rs b/library/std/src/io/buffered/bufreader.rs index e51dde994de4..fcb3e36027ba 100644 --- a/library/std/src/io/buffered/bufreader.rs +++ b/library/std/src/io/buffered/bufreader.rs @@ -74,6 +74,14 @@ impl BufReader { BufReader::with_capacity(DEFAULT_BUF_SIZE, inner) } + pub(crate) fn try_new_buffer() -> io::Result { + Buffer::try_with_capacity(DEFAULT_BUF_SIZE) + } + + pub(crate) fn with_buffer(inner: R, buf: Buffer) -> Self { + Self { inner, buf } + } + /// Creates a new `BufReader` with the specified buffer capacity. /// /// # Examples diff --git a/library/std/src/io/buffered/bufreader/buffer.rs b/library/std/src/io/buffered/bufreader/buffer.rs index 1bf84d8bef31..3df7e3971dac 100644 --- a/library/std/src/io/buffered/bufreader/buffer.rs +++ b/library/std/src/io/buffered/bufreader/buffer.rs @@ -10,7 +10,7 @@ //! without encountering any runtime bounds checks. use crate::cmp; -use crate::io::{self, BorrowedBuf, Read}; +use crate::io::{self, BorrowedBuf, ErrorKind, Read}; use crate::mem::MaybeUninit; pub struct Buffer { @@ -36,6 +36,16 @@ impl Buffer { Self { buf, pos: 0, filled: 0, initialized: 0 } } + #[inline] + pub fn try_with_capacity(capacity: usize) -> io::Result { + match Box::try_new_uninit_slice(capacity) { + Ok(buf) => Ok(Self { buf, pos: 0, filled: 0, initialized: 0 }), + Err(_) => { + Err(io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate read buffer")) + } + } + } + #[inline] pub fn buffer(&self) -> &[u8] { // SAFETY: self.pos and self.cap are valid, and self.cap => self.pos, and diff --git a/library/std/src/io/buffered/bufwriter.rs b/library/std/src/io/buffered/bufwriter.rs index 13516d3b961f..c41bae2aa4e8 100644 --- a/library/std/src/io/buffered/bufwriter.rs +++ b/library/std/src/io/buffered/bufwriter.rs @@ -94,6 +94,16 @@ impl BufWriter { BufWriter::with_capacity(DEFAULT_BUF_SIZE, inner) } + pub(crate) fn try_new_buffer() -> io::Result> { + Vec::try_with_capacity(DEFAULT_BUF_SIZE).map_err(|_| { + io::const_io_error!(ErrorKind::OutOfMemory, "failed to allocate write buffer") + }) + } + + pub(crate) fn with_buffer(inner: W, buf: Vec) -> Self { + Self { inner, buf, panicked: false } + } + /// Creates a new `BufWriter` with at least the specified buffer capacity. /// /// # Examples diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 795cc64e957d..f20814dd95cc 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -402,7 +402,7 @@ pub enum ErrorKind { /// The operation was partially successful and needs to be checked /// later on due to not blocking. - #[unstable(feature = "io_error_inprogress", issue = "none")] + #[unstable(feature = "io_error_inprogress", issue = "130840")] InProgress, // "Unusual" error kinds which do not correspond simply to (sets diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 4d93af6ea652..b81e7c18abbb 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -374,6 +374,7 @@ #![feature(slice_concat_trait)] #![feature(thin_box)] #![feature(try_reserve_kind)] +#![feature(try_with_capacity)] #![feature(vec_into_raw_parts)] // tidy-alphabetical-end // diff --git a/library/std/src/os/mod.rs b/library/std/src/os/mod.rs index a2496baa63fb..6701173d1e00 100644 --- a/library/std/src/os/mod.rs +++ b/library/std/src/os/mod.rs @@ -139,6 +139,8 @@ pub mod macos; pub mod netbsd; #[cfg(target_os = "nto")] pub mod nto; +#[cfg(target_os = "nuttx")] +pub mod nuttx; #[cfg(target_os = "openbsd")] pub mod openbsd; #[cfg(target_os = "redox")] diff --git a/library/std/src/os/nuttx/fs.rs b/library/std/src/os/nuttx/fs.rs new file mode 100644 index 000000000000..7d6d8d16eca7 --- /dev/null +++ b/library/std/src/os/nuttx/fs.rs @@ -0,0 +1,92 @@ +#![stable(feature = "metadata_ext", since = "1.1.0")] + +use crate::fs::Metadata; +use crate::sys_common::AsInner; + +#[stable(feature = "metadata_ext", since = "1.1.0")] +pub trait MetadataExt { + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_dev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ino(&self) -> u64; + #[stable(feature = "metadata_ext", since = "1.1.0")] + fn st_mode(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_nlink(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_uid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_gid(&self) -> u32; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_rdev(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_size(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_atime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_mtime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_ctime_nsec(&self) -> i64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blksize(&self) -> u64; + #[stable(feature = "metadata_ext2", since = "1.8.0")] + fn st_blocks(&self) -> u64; +} + +#[stable(feature = "metadata_ext", since = "1.1.0")] +impl MetadataExt for Metadata { + fn st_dev(&self) -> u64 { + self.as_inner().as_inner().st_dev as u64 + } + fn st_ino(&self) -> u64 { + self.as_inner().as_inner().st_ino as u64 + } + fn st_mode(&self) -> u32 { + self.as_inner().as_inner().st_mode as u32 + } + fn st_nlink(&self) -> u64 { + self.as_inner().as_inner().st_nlink as u64 + } + fn st_uid(&self) -> u32 { + self.as_inner().as_inner().st_uid as u32 + } + fn st_gid(&self) -> u32 { + self.as_inner().as_inner().st_gid as u32 + } + fn st_rdev(&self) -> u64 { + self.as_inner().as_inner().st_rdev as u64 + } + fn st_size(&self) -> u64 { + self.as_inner().as_inner().st_size as u64 + } + fn st_atime(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_sec as i64 + } + fn st_atime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_atim.tv_nsec as i64 + } + fn st_mtime(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_sec as i64 + } + fn st_mtime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_mtim.tv_nsec as i64 + } + fn st_ctime(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_sec as i64 + } + fn st_ctime_nsec(&self) -> i64 { + self.as_inner().as_inner().st_ctim.tv_nsec as i64 + } + fn st_blksize(&self) -> u64 { + self.as_inner().as_inner().st_blksize as u64 + } + fn st_blocks(&self) -> u64 { + self.as_inner().as_inner().st_blocks as u64 + } +} diff --git a/library/std/src/os/nuttx/mod.rs b/library/std/src/os/nuttx/mod.rs new file mode 100644 index 000000000000..7275bfd1765d --- /dev/null +++ b/library/std/src/os/nuttx/mod.rs @@ -0,0 +1,4 @@ +#![stable(feature = "raw_ext", since = "1.1.0")] +#![forbid(unsafe_op_in_unsafe_fn)] +pub mod fs; +pub(crate) mod raw; diff --git a/library/std/src/os/nuttx/raw.rs b/library/std/src/os/nuttx/raw.rs new file mode 100644 index 000000000000..113079cf4abd --- /dev/null +++ b/library/std/src/os/nuttx/raw.rs @@ -0,0 +1,33 @@ +//! rtems raw type definitions + +#![stable(feature = "raw_ext", since = "1.1.0")] +#![deprecated( + since = "1.8.0", + note = "these type aliases are no longer supported by \ + the standard library, the `libc` crate on \ + crates.io should be used instead for the correct \ + definitions" +)] +#![allow(deprecated)] + +#[stable(feature = "pthread_t", since = "1.8.0")] +pub type pthread_t = libc::pthread_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blkcnt_t = libc::blkcnt_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type blksize_t = libc::blksize_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type dev_t = libc::dev_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type ino_t = libc::ino_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type mode_t = libc::mode_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type nlink_t = libc::nlink_t; +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type off_t = libc::off_t; + +#[stable(feature = "raw_ext", since = "1.1.0")] +pub type time_t = libc::time_t; diff --git a/library/std/src/os/unix/mod.rs b/library/std/src/os/unix/mod.rs index 7d2f0bd4efea..5c2ec8ef994d 100644 --- a/library/std/src/os/unix/mod.rs +++ b/library/std/src/os/unix/mod.rs @@ -69,6 +69,8 @@ mod platform { pub use crate::os::netbsd::*; #[cfg(target_os = "nto")] pub use crate::os::nto::*; + #[cfg(target_os = "nuttx")] + pub use crate::os::nuttx::*; #[cfg(target_os = "openbsd")] pub use crate::os::openbsd::*; #[cfg(target_os = "redox")] diff --git a/library/std/src/random.rs b/library/std/src/random.rs index ecbf02eee843..604fa4df1106 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -71,7 +71,8 @@ impl RandomSource for DefaultRandomSource { /// /// This is a convenience function for `T::random(&mut DefaultRandomSource)` and /// will sample according to the same distribution as the underlying [`Random`] -/// trait implementation. +/// trait implementation. See [`DefaultRandomSource`] for more information about +/// how randomness is sourced. /// /// **Warning:** Be careful when manipulating random values! The /// [`random`](Random::random) method on integers samples them with a uniform diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 266b69cdc1e3..1af9d7662901 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -71,6 +71,7 @@ cfg_if::cfg_if! { } } else { #[inline] + #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); // We prefer posix_memalign over aligned_alloc since it is more widely available, and diff --git a/library/std/src/sys/dbg.rs b/library/std/src/sys/dbg.rs index 383ed84cfa25..7266a739e785 100644 --- a/library/std/src/sys/dbg.rs +++ b/library/std/src/sys/dbg.rs @@ -105,84 +105,7 @@ mod os { } } -#[cfg(target_os = "linux")] -mod os { - use super::DebuggerPresence; - use crate::fs::File; - use crate::io::Read; - - pub(super) fn is_debugger_present() -> Option { - // This function is crafted with the following goals: - // * Memory efficiency: It avoids crashing the panicking process due to - // out-of-memory (OOM) conditions by not using large heap buffers or - // allocating significant stack space, which could lead to stack overflow. - // * Minimal binary size: The function uses a minimal set of facilities - // from the standard library to avoid increasing the resulting binary size. - // - // To achieve these goals, the function does not use `[std::io::BufReader]` - // and instead reads the file byte by byte using a sliding window approach. - // It's important to note that the "/proc/self/status" pseudo-file is synthesized - // by the Virtual File System (VFS), meaning it is not read from a slow or - // non-volatile storage medium so buffering might not be as beneficial because - // all data is read from memory, though this approach does incur a syscall for - // each byte read. - // - // We cannot make assumptions about the file size or the position of the - // target prefix ("TracerPid:"), so the function does not use - // `[std::fs::read_to_string]` thus not employing UTF-8 to ASCII checking, - // conversion, or parsing as we're looking for an ASCII prefix. - // - // These condiderations make the function deviate from the familiar concise pattern - // of searching for a string in a text file. - - fn read_byte(file: &mut File) -> Option { - let mut buffer = [0]; - file.read_exact(&mut buffer).ok()?; - Some(buffer[0]) - } - - // The ASCII prefix of the datum we're interested in. - const TRACER_PID: &[u8] = b"TracerPid:\t"; - - let mut file = File::open("/proc/self/status").ok()?; - let mut matched = 0; - - // Look for the `TRACER_PID` prefix. - while let Some(byte) = read_byte(&mut file) { - if byte == TRACER_PID[matched] { - matched += 1; - if matched == TRACER_PID.len() { - break; - } - } else { - matched = 0; - } - } - - // Was the prefix found? - if matched != TRACER_PID.len() { - return None; - } - - // It was; get the ASCII representation of the first digit - // of the PID. That is enough to see if there is a debugger - // attached as the kernel does not pad the PID on the left - // with the leading zeroes. - let byte = read_byte(&mut file)?; - if byte.is_ascii_digit() && byte != b'0' { - Some(DebuggerPresence::Detected) - } else { - Some(DebuggerPresence::NotDetected) - } - } -} - -#[cfg(not(any( - target_os = "windows", - target_vendor = "apple", - target_os = "freebsd", - target_os = "linux" -)))] +#[cfg(not(any(target_os = "windows", target_vendor = "apple", target_os = "freebsd")))] mod os { pub(super) fn is_debugger_present() -> Option { None diff --git a/library/std/src/sys/pal/unix/args.rs b/library/std/src/sys/pal/unix/args.rs index a943e3a581a8..8438a61e90fe 100644 --- a/library/std/src/sys/pal/unix/args.rs +++ b/library/std/src/sys/pal/unix/args.rs @@ -113,6 +113,7 @@ impl DoubleEndedIterator for Args { target_os = "nto", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", ))] mod imp { use crate::ffi::c_char; diff --git a/library/std/src/sys/pal/unix/env.rs b/library/std/src/sys/pal/unix/env.rs index b2d399b8791b..2aee0b5d4605 100644 --- a/library/std/src/sys/pal/unix/env.rs +++ b/library/std/src/sys/pal/unix/env.rs @@ -283,3 +283,14 @@ pub mod os { pub const EXE_SUFFIX: &str = ""; pub const EXE_EXTENSION: &str = ""; } + +#[cfg(target_os = "nuttx")] +pub mod os { + pub const FAMILY: &str = "unix"; + pub const OS: &str = "nuttx"; + pub const DLL_PREFIX: &str = "lib"; + pub const DLL_SUFFIX: &str = ".so"; + pub const DLL_EXTENSION: &str = "so"; + pub const EXE_SUFFIX: &str = ""; + pub const EXE_EXTENSION: &str = ""; +} diff --git a/library/std/src/sys/pal/unix/fd.rs b/library/std/src/sys/pal/unix/fd.rs index 135042779ad8..6a28799ca55e 100644 --- a/library/std/src/sys/pal/unix/fd.rs +++ b/library/std/src/sys/pal/unix/fd.rs @@ -98,7 +98,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { let ret = cvt(unsafe { libc::readv( @@ -110,14 +115,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { io::default_read_vectored(|b| self.read(b), bufs) } #[inline] pub fn is_read_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } pub fn read_to_end(&self, buf: &mut Vec) -> io::Result { @@ -297,7 +312,12 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita")))] + #[cfg(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + )))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { let ret = cvt(unsafe { libc::writev( @@ -309,14 +329,24 @@ impl FileDesc { Ok(ret as usize) } - #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] + #[cfg(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))] pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { io::default_write_vectored(|b| self.write(b), bufs) } #[inline] pub fn is_write_vectored(&self) -> bool { - cfg!(not(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))) + cfg!(not(any( + target_os = "espidf", + target_os = "horizon", + target_os = "vita", + target_os = "nuttx" + ))) } #[cfg_attr(target_os = "vxworks", allow(unused_unsafe))] diff --git a/library/std/src/sys/pal/unix/fs.rs b/library/std/src/sys/pal/unix/fs.rs index dade6d6bbaee..86342c2add0e 100644 --- a/library/std/src/sys/pal/unix/fs.rs +++ b/library/std/src/sys/pal/unix/fs.rs @@ -479,6 +479,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn modified(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -501,7 +502,7 @@ impl FileAttr { SystemTime::new(self.stat.st_mtime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn modified(&self) -> io::Result { SystemTime::new(self.stat.st_mtim.tv_sec as i64, self.stat.st_mtim.tv_nsec as i64) } @@ -513,6 +514,7 @@ impl FileAttr { target_os = "vita", target_os = "hurd", target_os = "rtems", + target_os = "nuttx", )))] pub fn accessed(&self) -> io::Result { #[cfg(target_pointer_width = "32")] @@ -535,7 +537,7 @@ impl FileAttr { SystemTime::new(self.stat.st_atime as i64, 0) } - #[cfg(any(target_os = "horizon", target_os = "hurd"))] + #[cfg(any(target_os = "horizon", target_os = "hurd", target_os = "nuttx"))] pub fn accessed(&self) -> io::Result { SystemTime::new(self.stat.st_atim.tv_sec as i64, self.stat.st_atim.tv_nsec as i64) } @@ -866,6 +868,7 @@ impl Drop for Dir { target_os = "horizon", target_os = "vxworks", target_os = "rtems", + target_os = "nuttx", )))] { let fd = unsafe { libc::dirfd(self.0) }; @@ -1000,6 +1003,13 @@ impl DirEntry { self.entry.d_fileno as u64 } + #[cfg(target_os = "nuttx")] + pub fn ino(&self) -> u64 { + // Leave this 0 for now, as NuttX does not provide an inode number + // in its directory entries. + 0 + } + #[cfg(any( target_os = "netbsd", target_os = "openbsd", @@ -1327,7 +1337,8 @@ impl File { target_os = "redox", target_os = "espidf", target_os = "horizon", - target_os = "vxworks" + target_os = "vxworks", + target_os = "nuttx", )))] let to_timespec = |time: Option| match time { Some(time) if let Some(ts) = time.t.to_timespec() => Ok(ts), @@ -1342,7 +1353,7 @@ impl File { None => Ok(libc::timespec { tv_sec: 0, tv_nsec: libc::UTIME_OMIT as _ }), }; cfg_if::cfg_if! { - if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks"))] { + if #[cfg(any(target_os = "redox", target_os = "espidf", target_os = "horizon", target_os = "vxworks", target_os = "nuttx"))] { // Redox doesn't appear to support `UTIME_OMIT`. // ESP-IDF and HorizonOS do not support `futimens` at all and the behavior for those OS is therefore // the same as for Redox. diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index 0d63b1119d59..4fe18daa2040 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -222,6 +222,7 @@ static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = target_os = "horizon", target_os = "vxworks", target_os = "vita", + target_os = "nuttx", )))] pub(crate) fn on_broken_pipe_flag_used() -> bool { ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed) @@ -425,7 +426,7 @@ cfg_if::cfg_if! { } } -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod unsupported { use crate::io; diff --git a/library/std/src/sys/pal/unix/net.rs b/library/std/src/sys/pal/unix/net.rs index e98232ba89ac..0f2e015bbcdd 100644 --- a/library/std/src/sys/pal/unix/net.rs +++ b/library/std/src/sys/pal/unix/net.rs @@ -38,19 +38,19 @@ pub fn cvt_gai(err: c_int) -> io::Result<()> { // We may need to trigger a glibc workaround. See on_resolver_failure() for details. on_resolver_failure(); - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] if err == libc::EAI_SYSTEM { return Err(io::Error::last_os_error()); } - #[cfg(not(target_os = "espidf"))] + #[cfg(not(any(target_os = "espidf", target_os = "nuttx")))] let detail = unsafe { // We can't always expect a UTF-8 environment. When we don't get that luxury, // it's better to give a low-quality error message than none at all. CStr::from_ptr(libc::gai_strerror(err)).to_string_lossy() }; - #[cfg(target_os = "espidf")] + #[cfg(any(target_os = "espidf", target_os = "nuttx"))] let detail = ""; Err(io::Error::new( diff --git a/library/std/src/sys/pal/unix/os.rs b/library/std/src/sys/pal/unix/os.rs index 503f8915256e..d99bde2f9a50 100644 --- a/library/std/src/sys/pal/unix/os.rs +++ b/library/std/src/sys/pal/unix/os.rs @@ -48,6 +48,7 @@ extern "C" { target_os = "openbsd", target_os = "android", target_os = "redox", + target_os = "nuttx", target_env = "newlib" ), link_name = "__errno" @@ -399,6 +400,7 @@ pub fn current_exe() -> io::Result { target_os = "linux", target_os = "hurd", target_os = "android", + target_os = "nuttx", target_os = "emscripten" ))] pub fn current_exe() -> io::Result { @@ -717,6 +719,7 @@ pub fn home_dir() -> Option { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), ))] unsafe fn fallback() -> Option { @@ -730,6 +733,7 @@ pub fn home_dir() -> Option { target_os = "espidf", target_os = "horizon", target_os = "vita", + target_os = "nuttx", all(target_vendor = "apple", not(target_os = "macos")), )))] unsafe fn fallback() -> Option { diff --git a/library/std/src/sys/pal/unix/process/mod.rs b/library/std/src/sys/pal/unix/process/mod.rs index 074f0a105e32..2751d51c44d2 100644 --- a/library/std/src/sys/pal/unix/process/mod.rs +++ b/library/std/src/sys/pal/unix/process/mod.rs @@ -2,10 +2,10 @@ pub use self::process_common::{Command, CommandArgs, ExitCode, Stdio, StdioPipes pub use self::process_inner::{ExitStatus, ExitStatusError, Process}; pub use crate::ffi::OsString as EnvKey; -#[cfg_attr(any(target_os = "espidf", target_os = "horizon"), allow(unused))] +#[cfg_attr(any(target_os = "espidf", target_os = "horizon", target_os = "nuttx"), allow(unused))] mod process_common; -#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] +#[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] mod process_unsupported; cfg_if::cfg_if! { @@ -16,7 +16,7 @@ cfg_if::cfg_if! { } else if #[cfg(target_os = "vxworks")] { #[path = "process_vxworks.rs"] mod process_inner; - } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita"))] { + } else if #[cfg(any(target_os = "espidf", target_os = "horizon", target_os = "vita", target_os = "nuttx"))] { mod process_inner { pub use super::process_unsupported::*; } diff --git a/library/std/src/sys/pal/unix/thread.rs b/library/std/src/sys/pal/unix/thread.rs index 7fe9b6c3e52f..cb6133274d9c 100644 --- a/library/std/src/sys/pal/unix/thread.rs +++ b/library/std/src/sys/pal/unix/thread.rs @@ -140,7 +140,12 @@ impl Thread { } } - #[cfg(any(target_os = "freebsd", target_os = "dragonfly", target_os = "openbsd"))] + #[cfg(any( + target_os = "freebsd", + target_os = "dragonfly", + target_os = "openbsd", + target_os = "nuttx" + ))] pub fn set_name(name: &CStr) { unsafe { libc::pthread_set_name_np(libc::pthread_self(), name.as_ptr()); @@ -517,7 +522,7 @@ mod cgroups { use crate::borrow::Cow; use crate::ffi::OsString; use crate::fs::{File, exists}; - use crate::io::{BufRead, BufReader, Read}; + use crate::io::{BufRead, Read}; use crate::os::unix::ffi::OsStringExt; use crate::path::{Path, PathBuf}; use crate::str::from_utf8; @@ -690,7 +695,7 @@ mod cgroups { /// If the cgroupfs is a bind mount then `group_path` is adjusted to skip /// over the already-included prefix fn find_mountpoint(group_path: &Path) -> Option<(Cow<'static, str>, &Path)> { - let mut reader = BufReader::new(File::open("/proc/self/mountinfo").ok()?); + let mut reader = File::open_buffered("/proc/self/mountinfo").ok()?; let mut line = String::with_capacity(256); loop { line.clear(); @@ -747,12 +752,15 @@ unsafe fn min_stack_size(attr: *const libc::pthread_attr_t) -> usize { } // No point in looking up __pthread_get_minstack() on non-glibc platforms. -#[cfg(all(not(all(target_os = "linux", target_env = "gnu")), not(target_os = "netbsd")))] +#[cfg(all( + not(all(target_os = "linux", target_env = "gnu")), + not(any(target_os = "netbsd", target_os = "nuttx")) +))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { libc::PTHREAD_STACK_MIN } -#[cfg(target_os = "netbsd")] +#[cfg(any(target_os = "netbsd", target_os = "nuttx"))] unsafe fn min_stack_size(_: *const libc::pthread_attr_t) -> usize { static STACK: crate::sync::OnceLock = crate::sync::OnceLock::new(); diff --git a/library/std/src/sys/personality/mod.rs b/library/std/src/sys/personality/mod.rs index 68085d026c40..9754e840d151 100644 --- a/library/std/src/sys/personality/mod.rs +++ b/library/std/src/sys/personality/mod.rs @@ -31,7 +31,7 @@ cfg_if::cfg_if! { target_os = "psp", target_os = "xous", target_os = "solid_asp3", - all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems")), + all(target_family = "unix", not(target_os = "espidf"), not(target_os = "l4re"), not(target_os = "rtems"), not(target_os = "nuttx")), all(target_vendor = "fortanix", target_env = "sgx"), ))] { mod gcc; diff --git a/library/std/src/sys/random/mod.rs b/library/std/src/sys/random/mod.rs index 16fb8c64c9b8..d625814d15b3 100644 --- a/library/std/src/sys/random/mod.rs +++ b/library/std/src/sys/random/mod.rs @@ -42,6 +42,7 @@ cfg_if::cfg_if! { target_os = "hurd", target_os = "l4re", target_os = "nto", + target_os = "nuttx", ))] { mod unix_legacy; pub use unix_legacy::fill_bytes; diff --git a/library/std/src/sys_common/net.rs b/library/std/src/sys_common/net.rs index 85a82b088cd4..57f07d05cae3 100644 --- a/library/std/src/sys_common/net.rs +++ b/library/std/src/sys_common/net.rs @@ -21,6 +21,7 @@ cfg_if::cfg_if! { target_os = "haiku", target_os = "l4re", target_os = "nto", + target_os = "nuttx", target_vendor = "apple", ))] { use crate::sys::net::netc::IPV6_JOIN_GROUP as IPV6_ADD_MEMBERSHIP; diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index ebbe50cc6515..4b2c65cfdf54 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -18,6 +18,7 @@ #![doc(test(attr(deny(warnings))))] #![doc(rust_logo)] #![feature(rustdoc_internals)] +#![feature(file_buffered)] #![feature(internal_output_capture)] #![feature(staged_api)] #![feature(process_exitcode_internals)] diff --git a/library/test/src/term/terminfo/mod.rs b/library/test/src/term/terminfo/mod.rs index ac10ec2b850e..974b8afd598d 100644 --- a/library/test/src/term/terminfo/mod.rs +++ b/library/test/src/term/terminfo/mod.rs @@ -3,9 +3,8 @@ use std::collections::HashMap; use std::fs::File; use std::io::prelude::*; -use std::io::{self, BufReader}; use std::path::Path; -use std::{env, error, fmt}; +use std::{env, error, fmt, io}; use parm::{Param, Variables, expand}; use parser::compiled::{msys_terminfo, parse}; @@ -102,8 +101,7 @@ impl TermInfo { } // Keep the metadata small fn _from_path(path: &Path) -> Result { - let file = File::open(path).map_err(Error::IoError)?; - let mut reader = BufReader::new(file); + let mut reader = File::open_buffered(path).map_err(Error::IoError)?; parse(&mut reader, false).map_err(Error::MalformedTerminfo) } } diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index 26ed00bfbd53..46026324d2f8 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -23,6 +23,7 @@ cfg_if::cfg_if! { target_os = "none", target_os = "espidf", target_os = "rtems", + target_os = "nuttx", ))] { // These "unix" family members do not have unwinder. } else if #[cfg(any( diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md index 999eb9b8a7c6..207eb5d6d4f7 100644 --- a/src/doc/rustc/src/platform-support.md +++ b/src/doc/rustc/src/platform-support.md @@ -358,12 +358,14 @@ target | std | host | notes [`riscv32imc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imac-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF [`riscv32imafc-esp-espidf`](platform-support/esp-idf.md) | ✓ | | RISC-V ESP-IDF +[`riscv32-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | [`riscv64gc-unknown-hermit`](platform-support/hermit.md) | ✓ | | RISC-V Hermit `riscv64gc-unknown-freebsd` | | | RISC-V FreeBSD `riscv64gc-unknown-fuchsia` | | | RISC-V Fuchsia [`riscv64gc-unknown-netbsd`](platform-support/netbsd.md) | ✓ | ✓ | RISC-V NetBSD [`riscv64gc-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | OpenBSD/riscv64 [`riscv64-linux-android`](platform-support/android.md) | | | RISC-V 64-bit Android +[`riscv64-wrs-vxworks`](platform-support/vxworks.md) | ✓ | | `s390x-unknown-linux-musl` | | | S390x Linux (kernel 3.2, musl 1.2.3) `sparc-unknown-linux-gnu` | ✓ | | 32-bit SPARC Linux [`sparc-unknown-none-elf`](./platform-support/sparc-unknown-none-elf.md) | * | | Bare 32-bit SPARC V7+ diff --git a/src/doc/rustc/src/platform-support/vxworks.md b/src/doc/rustc/src/platform-support/vxworks.md index f1860346c8f2..6aa3d8b73618 100644 --- a/src/doc/rustc/src/platform-support/vxworks.md +++ b/src/doc/rustc/src/platform-support/vxworks.md @@ -14,6 +14,8 @@ Target triplets available: - `powerpc-wrs-vxworks` - `powerpc64-wrs-vxworks` - `powerpc-wrs-vxworks-spe` +- `riscv32-wrs-vxworks` +- `riscv64-wrs-vxworks` ## Target maintainers diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index 162537992157..12246b0d416a 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -16,7 +16,7 @@ use std::cell::RefCell; use std::ffi::OsString; use std::fs::File; -use std::io::{self, BufWriter, Write as _}; +use std::io::{self, Write as _}; use std::iter::once; use std::marker::PhantomData; use std::path::{Component, Path, PathBuf}; @@ -1020,8 +1020,7 @@ where for part in parts { template.append(part); } - let file = try_err!(File::create(&path), &path); - let mut file = BufWriter::new(file); + let mut file = try_err!(File::create_buffered(&path), &path); try_err!(write!(file, "{template}"), &path); try_err!(file.flush(), &path); } diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 7f82fb5c6865..b7a683eed1c2 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -286,7 +286,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { self.serialize_and_write( output_crate, - BufWriter::new(try_err!(File::create(&p), p)), + try_err!(File::create_buffered(&p), p), &p.display().to_string(), ) } else { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index dd75f4d7928a..2be415e2e0ef 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -5,6 +5,7 @@ #![feature(rustc_private)] #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(file_buffered)] #![feature(if_let_guard)] #![feature(impl_trait_in_assoc_type)] #![feature(iter_intersperse)] diff --git a/src/tools/clippy/.github/deploy.sh b/src/tools/clippy/.github/deploy.sh index d937661c0f82..5b4b4be4e36b 100644 --- a/src/tools/clippy/.github/deploy.sh +++ b/src/tools/clippy/.github/deploy.sh @@ -25,16 +25,15 @@ if [[ $BETA = "true" ]]; then fi # Generate version index that is shown as root index page -cp util/gh-pages/versions.html out/index.html - -echo "Making the versions.json file" -python3 ./util/versions.py out +python3 ./util/versions.py ./util/gh-pages/versions.html out # Now let's go have some fun with the cloned repo cd out git config user.name "GHA CI" git config user.email "gha@ci.invalid" +git status + if [[ -n $TAG_NAME ]]; then # track files, so that the following check works git add --intent-to-add "$TAG_NAME" @@ -46,8 +45,6 @@ if [[ -n $TAG_NAME ]]; then git add "$TAG_NAME" # Update the symlink git add stable - # Update versions file - git add versions.json git commit -m "Add documentation for ${TAG_NAME} release: ${SHA}" elif [[ $BETA = "true" ]]; then if git diff --exit-code --quiet -- beta/; then diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml index 2aa13313fa51..026771e6fcf4 100644 --- a/src/tools/clippy/.github/workflows/clippy_bors.yml +++ b/src/tools/clippy/.github/workflows/clippy_bors.yml @@ -162,7 +162,7 @@ jobs: find $DIR ! -executable -o -type d ! -path $DIR | xargs rm -rf - name: Upload Binaries - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: binaries path: target/debug @@ -202,7 +202,7 @@ jobs: # Download - name: Download target dir - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: binaries path: target/debug diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 9bc4ad9698db..41a86e8ce510 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6,7 +6,61 @@ document. ## Unreleased / Beta / In Rust Nightly -[c9139bd5...master](https://github.com/rust-lang/rust-clippy/compare/c9139bd5...master) +[b794b8e0...master](https://github.com/rust-lang/rust-clippy/compare/b794b8e0...master) + +## Rust 1.81 + +Current stable, released 2024-09-05 + +### New Lints + +* Added [`cfg_not_test`] to `restriction` + [#11293](https://github.com/rust-lang/rust-clippy/pull/11293) +* Added [`byte_char_slices`] to `style` + [#10155](https://github.com/rust-lang/rust-clippy/pull/10155) +* Added [`set_contains_or_insert`] to `nursery` + [#12873](https://github.com/rust-lang/rust-clippy/pull/12873) +* Added [`manual_rotate`] to `style` + [#12983](https://github.com/rust-lang/rust-clippy/pull/12983) +* Added [`unnecessary_min_or_max`] to `complexity` + [#12368](https://github.com/rust-lang/rust-clippy/pull/12368) +* Added [`manual_inspect`] to `complexity` + [#12287](https://github.com/rust-lang/rust-clippy/pull/12287) +* Added [`field_scoped_visibility_modifiers`] to `restriction` + [#12893](https://github.com/rust-lang/rust-clippy/pull/12893) +* Added [`manual_pattern_char_comparison`] to `style` + [#12849](https://github.com/rust-lang/rust-clippy/pull/12849) +* Added [`needless_maybe_sized`] to `suspicious` + [#10632](https://github.com/rust-lang/rust-clippy/pull/10632) +* Added [`needless_character_iteration`] to `suspicious` + [#12815](https://github.com/rust-lang/rust-clippy/pull/12815) + +### Moves and Deprecations + +* [`allow_attributes`], [`allow_attributes_without_reason`]: Now work on stable + [rust#120924](https://github.com/rust-lang/rust/pull/120924) +* Renamed `overflow_check_conditional` to [`panicking_overflow_checks`] + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Moved [`panicking_overflow_checks`] to `correctness` (From `complexity` now deny-by-default) + [#12944](https://github.com/rust-lang/rust-clippy/pull/12944) +* Renamed `thread_local_initializer_can_be_made_const` to [`missing_const_for_thread_local`] + [#12974](https://github.com/rust-lang/rust-clippy/pull/12974) +* Deprecated [`maybe_misused_cfg`] and [`mismatched_target_os`] as they are now caught by cargo + and rustc + [#12875](https://github.com/rust-lang/rust-clippy/pull/12875) + +### Enhancements + +* [`significant_drop_in_scrutinee`]: Now also checks scrutinies of `while let` and `for let` + expressions + [#12870](https://github.com/rust-lang/rust-clippy/pull/12870) +* [`std_instead_of_core`]: Now respects the `msrv` configuration + [#13168](https://github.com/rust-lang/rust-clippy/pull/13168) + +### ICE Fixes + +* [`suboptimal_flops`]: No longer crashes on custom `.log()` functions + [#12884](https://github.com/rust-lang/rust-clippy/pull/12884) ## Rust 1.80 @@ -5500,6 +5554,7 @@ Released 2018-09-13 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons [`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked +[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements @@ -5559,6 +5614,7 @@ Released 2018-09-13 [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits [`manual_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp +[`manual_div_ceil`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_div_ceil [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map [`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find @@ -5570,6 +5626,7 @@ Released 2018-09-13 [`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check [`manual_is_finite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_finite [`manual_is_infinite`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_infinite +[`manual_is_power_of_two`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_power_of_two [`manual_is_variant_and`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_variant_and [`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else [`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str @@ -5716,6 +5773,7 @@ Released 2018-09-13 [`non_minimal_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_minimal_cfg [`non_octal_unix_permissions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_octal_unix_permissions [`non_send_fields_in_send_ty`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_send_fields_in_send_ty +[`non_zero_suggestions`]: https://rust-lang.github.io/rust-clippy/master/index.html#non_zero_suggestions [`nonminimal_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonminimal_bool [`nonsensical_open_options`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonsensical_open_options [`nonstandard_macro_braces`]: https://rust-lang.github.io/rust-clippy/master/index.html#nonstandard_macro_braces @@ -5757,6 +5815,7 @@ Released 2018-09-13 [`pathbuf_init_then_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#pathbuf_init_then_push [`pattern_type_mismatch`]: https://rust-lang.github.io/rust-clippy/master/index.html#pattern_type_mismatch [`permissions_set_readonly_false`]: https://rust-lang.github.io/rust-clippy/master/index.html#permissions_set_readonly_false +[`pointers_in_nomem_asm_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#pointers_in_nomem_asm_block [`positional_named_format_parameters`]: https://rust-lang.github.io/rust-clippy/master/index.html#positional_named_format_parameters [`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma [`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence @@ -5962,6 +6021,7 @@ Released 2018-09-13 [`unnecessary_fallible_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fallible_conversions [`unnecessary_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_filter_map [`unnecessary_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_find_map +[`unnecessary_first_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_first_then_check [`unnecessary_fold`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_fold [`unnecessary_get_then_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_get_then_check [`unnecessary_join`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_join @@ -6003,6 +6063,7 @@ Released 2018-09-13 [`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self +[`unused_trait_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit [`unusual_byte_groupings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unusual_byte_groupings [`unwrap_in_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#unwrap_in_result @@ -6013,6 +6074,7 @@ Released 2018-09-13 [`use_debug`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_debug [`use_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#use_self [`used_underscore_binding`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_binding +[`used_underscore_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#used_underscore_items [`useless_asref`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_asref [`useless_attribute`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_attribute [`useless_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#useless_conversion @@ -6047,6 +6109,7 @@ Released 2018-09-13 [`zero_repeat_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_repeat_side_effects [`zero_sized_map_values`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_sized_map_values [`zero_width_space`]: https://rust-lang.github.io/rust-clippy/master/index.html#zero_width_space +[`zombie_processes`]: https://rust-lang.github.io/rust-clippy/master/index.html#zombie_processes [`zst_offset`]: https://rust-lang.github.io/rust-clippy/master/index.html#zst_offset diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index b48b881097f4..cf810798d8cc 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy" -version = "0.1.82" +version = "0.1.83" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" @@ -31,7 +31,7 @@ anstream = "0.6.0" [dev-dependencies] cargo_metadata = "0.18.1" -ui_test = "0.25" +ui_test = "0.26.4" regex = "1.5.5" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.122" diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index 78348797588a..91159bc79c51 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -727,6 +727,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`uninlined_format_args`](https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args) * [`unnecessary_lazy_evaluations`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_lazy_evaluations) * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns) +* [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names) * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self) diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml index 5c4e0761dbca..9da7112345de 100644 --- a/src/tools/clippy/clippy_config/Cargo.toml +++ b/src/tools/clippy/clippy_config/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_config" -version = "0.1.82" +version = "0.1.83" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index a6f1b958bfb1..757620341ccc 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,6 +1,6 @@ +use crate::ClippyConfiguration; use crate::msrvs::Msrv; use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename}; -use crate::ClippyConfiguration; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -563,6 +563,7 @@ define_Conf! { uninlined_format_args, unnecessary_lazy_evaluations, unnested_or_patterns, + unused_trait_names, use_self, )] msrv: Msrv = Msrv::empty(), @@ -864,7 +865,7 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec) { cmp::max(1, terminal_width / (SEPARATOR_WIDTH + max_field_width)) }); - let rows = (fields.len() + (columns - 1)) / columns; + let rows = fields.len().div_ceil(columns); let column_widths = (0..columns) .map(|column| { diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs index ac838cc81d2f..c63d98a0a13f 100644 --- a/src/tools/clippy/clippy_config/src/lib.rs +++ b/src/tools/clippy/clippy_config/src/lib.rs @@ -26,5 +26,5 @@ mod metadata; pub mod msrvs; pub mod types; -pub use conf::{get_configuration_metadata, lookup_conf_file, Conf}; +pub use conf::{Conf, get_configuration_metadata, lookup_conf_file}; pub use metadata::ClippyConfiguration; diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs index a2b4b6e256fa..e30df3d32341 100644 --- a/src/tools/clippy/clippy_config/src/msrvs.rs +++ b/src/tools/clippy/clippy_config/src/msrvs.rs @@ -1,7 +1,7 @@ use rustc_ast::Attribute; use rustc_attr::parse_version; use rustc_session::{RustcVersion, Session}; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use serde::Deserialize; use std::fmt; @@ -23,6 +23,7 @@ msrv_aliases! { 1,80,0 { BOX_INTO_ITER} 1,77,0 { C_STR_LITERALS } 1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT } + 1,73,0 { MANUAL_DIV_CEIL } 1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE } 1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN } 1,68,0 { PATH_MAIN_SEPARATOR_STR } @@ -38,7 +39,7 @@ msrv_aliases! { 1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST } 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS } 1,50,0 { BOOL_THEN, CLAMP } - 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN } + 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN, SATURATING_SUB_CONST } 1,46,0 { CONST_IF_MATCH } 1,45,0 { STR_STRIP_PREFIX } 1,43,0 { LOG2_10, LOG10_2, NUMERIC_ASSOCIATED_CONSTANTS } @@ -50,6 +51,7 @@ msrv_aliases! { 1,36,0 { ITERATOR_COPIED } 1,35,0 { OPTION_COPIED, RANGE_CONTAINS } 1,34,0 { TRY_FROM } + 1,33,0 { UNDERSCORE_IMPORTS } 1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES } 1,29,0 { ITER_FLATTEN } 1,28,0 { FROM_BOOL } diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index d47e34bb5bce..bab63911182d 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -1,5 +1,5 @@ use serde::de::{self, Deserializer, Visitor}; -use serde::{ser, Deserialize, Serialize}; +use serde::{Deserialize, Serialize, ser}; use std::fmt; #[derive(Debug, Deserialize)] diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs index 5fc4365c6e78..8c61c35533cf 100644 --- a/src/tools/clippy/clippy_dev/src/fmt.rs +++ b/src/tools/clippy/clippy_dev/src/fmt.rs @@ -1,6 +1,6 @@ use crate::clippy_project_root; use itertools::Itertools; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use shell_escape::escape; use std::ffi::{OsStr, OsString}; use std::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs index 87117832fb95..5fd65017cfb9 100644 --- a/src/tools/clippy/clippy_dev/src/new_lint.rs +++ b/src/tools/clippy/clippy_dev/src/new_lint.rs @@ -441,7 +441,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R #[allow(clippy::too_many_lines)] fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str> { - use super::update_lints::{match_tokens, LintDeclSearchResult}; + use super::update_lints::{LintDeclSearchResult, match_tokens}; use rustc_lexer::TokenKind; let lint_name_upper = lint.name.to_uppercase(); diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs index 19560b31fd3e..cc14cd8dae69 100644 --- a/src/tools/clippy/clippy_dev/src/serve.rs +++ b/src/tools/clippy/clippy_dev/src/serve.rs @@ -29,7 +29,7 @@ pub fn run(port: u16, lint: Option) -> ! { } if let Some(url) = url.take() { thread::spawn(move || { - Command::new(PYTHON) + let mut child = Command::new(PYTHON) .arg("-m") .arg("http.server") .arg(port.to_string()) @@ -40,6 +40,7 @@ pub fn run(port: u16, lint: Option) -> ! { thread::sleep(Duration::from_millis(500)); // Launch browser after first export.py has completed and http.server is up let _result = opener::open(url); + child.wait().unwrap(); }); } thread::sleep(Duration::from_millis(1000)); diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs index 8dbda1c634c2..d6ed36d52f4e 100644 --- a/src/tools/clippy/clippy_dev/src/update_lints.rs +++ b/src/tools/clippy/clippy_dev/src/update_lints.rs @@ -1,7 +1,7 @@ use crate::clippy_project_root; use aho_corasick::AhoCorasickBuilder; use itertools::Itertools; -use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind}; +use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape}; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; use std::fmt::{self, Write}; @@ -1048,23 +1048,17 @@ mod tests { Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), ]; let mut expected: HashMap> = HashMap::new(); - expected.insert( - "group1".to_string(), - vec![ - Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), - Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), - ], - ); - expected.insert( - "group2".to_string(), - vec![Lint::new( - "should_assert_eq2", - "group2", - "\"abc\"", - "module_name", - Range::default(), - )], - ); + expected.insert("group1".to_string(), vec![ + Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()), + Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()), + ]); + expected.insert("group2".to_string(), vec![Lint::new( + "should_assert_eq2", + "group2", + "\"abc\"", + "module_name", + Range::default(), + )]); assert_eq!(expected, Lint::by_lint_group(lints.into_iter())); } } diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml index fbd4566da58c..d1188940b46a 100644 --- a/src/tools/clippy/clippy_lints/Cargo.toml +++ b/src/tools/clippy/clippy_lints/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_lints" -version = "0.1.82" +version = "0.1.83" description = "A bunch of helpful lints to avoid common pitfalls in Rust" repository = "https://github.com/rust-lang/rust-clippy" readme = "README.md" diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index c72b3f1318c8..1af6d448a93c 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -3,12 +3,12 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX}; +use rustc_hir::def_id::{CRATE_DEF_INDEX, DefId}; use rustc_hir::{HirId, ItemKind, Node, Path}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Symbol; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs index 451bae959874..370f0c482fd5 100644 --- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs +++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{trim_span, walk_span_to_context}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 56f5e903dc3e..4f8f091a0956 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -1,10 +1,10 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::{impl_lint_pass, RustcVersion}; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::symbol; use std::f64::consts as f64; diff --git a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs index 4eafa330fafa..2643f850879c 100644 --- a/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs +++ b/src/tools/clippy/clippy_lints/src/arc_with_non_send_sync.rs @@ -4,8 +4,8 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::GenericArgKind; +use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_session::declare_lint_pass; use rustc_span::symbol::sym; diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 7eaac80f969f..b6684825835a 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_inside_always_const_context; -use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs index f1cb4a05af86..c073dee855e2 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_node}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item}; use clippy_utils::usage::local_used_after_expr; diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs index 55645d04eef3..0b82c0cd04c1 100644 --- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs +++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs @@ -1,7 +1,7 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap}; +use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir}; use clippy_utils::sugg::Sugg; use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs index 4ab97118df1d..40959eccd3a9 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs @@ -1,4 +1,4 @@ -use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON}; +use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_ast::{MetaItemKind, NestedMetaItem}; @@ -26,7 +26,7 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet cx, ALLOW_ATTRIBUTES_WITHOUT_REASON, attr.span, - format!("`{}` attribute without specifying a reason", name.as_str()), + format!("`{name}` attribute without specifying a reason"), |diag| { diag.help("try adding a reason at the end with `, reason = \"..\"`"); }, diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs index 9b08fd6d654a..508963a20ea7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs @@ -1,10 +1,10 @@ -use super::utils::extract_clippy_lint; use super::BLANKET_CLIPPY_RESTRICTION_LINTS; +use super::utils::extract_clippy_lint; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use rustc_ast::NestedMetaItem; use rustc_lint::{LateContext, Level, LintContext}; use rustc_span::symbol::Symbol; -use rustc_span::{sym, DUMMY_SP}; +use rustc_span::{DUMMY_SP, sym}; pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) { for lint in items { diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs index e872ab6b4b5f..abf924f7542e 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs @@ -1,4 +1,4 @@ -use super::{unnecessary_clippy_cfg, Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR}; +use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::AttrStyle; diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs index 40a1c4e28842..55f8e1072db7 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::{Attribute, MetaItem}; use rustc_data_structures::fx::FxHashMap; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::collections::hash_map::Entry; fn emit_if_duplicated( @@ -36,7 +36,7 @@ fn check_duplicated_attr( } let Some(ident) = attr.ident() else { return }; let name = ident.name; - if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented { + if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented || name == sym::reason { // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg // conditions are the same. // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected. diff --git a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs deleted file mode 100644 index 7ff644b4c445..000000000000 --- a/src/tools/clippy/clippy_lints/src/attrs/empty_line_after.rs +++ /dev/null @@ -1,52 +0,0 @@ -use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; -use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt}; -use rustc_ast::{AttrKind, AttrStyle}; -use rustc_lint::EarlyContext; -use rustc_span::Span; - -/// Check for empty lines after outer attributes. -/// -/// Attributes and documentation comments are both considered outer attributes -/// by the AST. However, the average user likely considers them to be different. -/// Checking for empty lines after each of these attributes is split into two different -/// lints but can share the same logic. -pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - let mut iter = item.attrs.iter().peekable(); - while let Some(attr) = iter.next() { - if (matches!(attr.kind, AttrKind::Normal(..)) || matches!(attr.kind, AttrKind::DocComment(..))) - && attr.style == AttrStyle::Outer - && is_present_in_source(cx, attr.span) - { - let begin_of_attr_to_item = Span::new(attr.span.lo(), item.span.lo(), item.span.ctxt(), item.span.parent()); - let end_of_attr_to_next_attr_or_item = Span::new( - attr.span.hi(), - iter.peek().map_or(item.span.lo(), |next_attr| next_attr.span.lo()), - item.span.ctxt(), - item.span.parent(), - ); - - if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) { - let lines = snippet.split('\n').collect::>(); - let lines = without_block_comments(lines); - - if lines.iter().filter(|l| l.trim().is_empty()).count() > 2 { - let (lint_msg, lint_type) = match attr.kind { - AttrKind::DocComment(..) => ( - "found an empty line after a doc comment. \ - Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`?", - EMPTY_LINE_AFTER_DOC_COMMENTS, - ), - AttrKind::Normal(..) => ( - "found an empty line after an outer attribute. \ - Perhaps you forgot to add a `!` to make it an inner attribute?", - EMPTY_LINE_AFTER_OUTER_ATTR, - ), - }; - - span_lint(cx, lint_type, begin_of_attr_to_item, lint_msg); - } - } - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs index 3b5b80ffefaf..d41bb580c6cd 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/inline_always.rs @@ -1,10 +1,10 @@ -use super::utils::is_word; use super::INLINE_ALWAYS; +use super::utils::is_word; use clippy_utils::diagnostics::span_lint; use rustc_ast::Attribute; use rustc_lint::LateContext; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub(super) fn check(cx: &LateContext<'_>, span: Span, name: Symbol, attrs: &[Attribute]) { if span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index 3b14e9aee7fc..888f28fa2258 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -4,7 +4,6 @@ mod blanket_clippy_restriction_lints; mod deprecated_cfg_attr; mod deprecated_semver; mod duplicated_attributes; -mod empty_line_after; mod inline_always; mod mixed_attributes_style; mod non_minimal_cfg; @@ -13,8 +12,8 @@ mod unnecessary_clippy_cfg; mod useless_attribute; mod utils; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem}; use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; @@ -126,94 +125,6 @@ declare_clippy_lint! { "use of `#[deprecated(since = \"x\")]` where x is not semver" } -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after outer attributes - /// - /// ### Why is this bad? - /// Most likely the attribute was meant to be an inner attribute using a '!'. - /// If it was meant to be an outer attribute, then the following item - /// should not be separated by empty lines. - /// - /// ### Known problems - /// Can cause false positives. - /// - /// From the clippy side it's difficult to detect empty lines between an attributes and the - /// following item because empty lines and comments are not part of the AST. The parsing - /// currently works for basic cases but is not perfect. - /// - /// ### Example - /// ```no_run - /// #[allow(dead_code)] - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// // Good (as inner attribute) - /// #![allow(dead_code)] - /// - /// fn this_is_fine() { } - /// - /// // or - /// - /// // Good (as outer attribute) - /// #[allow(dead_code)] - /// fn this_is_fine_too() { } - /// ``` - #[clippy::version = "pre 1.29.0"] - pub EMPTY_LINE_AFTER_OUTER_ATTR, - nursery, - "empty line after outer attribute" -} - -declare_clippy_lint! { - /// ### What it does - /// Checks for empty lines after documentation comments. - /// - /// ### Why is this bad? - /// The documentation comment was most likely meant to be an inner attribute or regular comment. - /// If it was intended to be a documentation comment, then the empty line should be removed to - /// be more idiomatic. - /// - /// ### Known problems - /// Only detects empty lines immediately following the documentation. If the doc comment is followed - /// by an attribute and then an empty line, this lint will not trigger. Use `empty_line_after_outer_attr` - /// in combination with this lint to detect both cases. - /// - /// Does not detect empty lines after doc attributes (e.g. `#[doc = ""]`). - /// - /// ### Example - /// ```no_run - /// /// Some doc comment with a blank line after it. - /// - /// fn not_quite_good_code() { } - /// ``` - /// - /// Use instead: - /// ```no_run - /// /// Good (no blank line) - /// fn this_is_fine() { } - /// ``` - /// - /// ```no_run - /// // Good (convert to a regular comment) - /// - /// fn this_is_fine_too() { } - /// ``` - /// - /// ```no_run - /// //! Good (convert to a comment on an inner attribute) - /// - /// fn this_is_fine_as_well() { } - /// ``` - #[clippy::version = "1.70.0"] - pub EMPTY_LINE_AFTER_DOC_COMMENTS, - nursery, - "empty line after documentation comments" -} - declare_clippy_lint! { /// ### What it does /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. @@ -601,18 +512,12 @@ impl EarlyAttributes { impl_lint_pass!(EarlyAttributes => [ DEPRECATED_CFG_ATTR, - EMPTY_LINE_AFTER_OUTER_ATTR, - EMPTY_LINE_AFTER_DOC_COMMENTS, NON_MINIMAL_CFG, DEPRECATED_CLIPPY_CFG_ATTR, UNNECESSARY_CLIPPY_CFG, ]); impl EarlyLintPass for EarlyAttributes { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &rustc_ast::Item) { - empty_line_after::check(cx, item); - } - fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { deprecated_cfg_attr::check(cx, attr, &self.msrv); deprecated_cfg_attr::check_clippy(cx, attr); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 67ba605a59fa..12668c616c13 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word}; use super::{Attribute, USELESS_ATTRIBUTE}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{first_line_of_span, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, first_line_of_span}; use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; @@ -51,6 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) | "module_name_repetitions" | "single_component_path_imports" | "disallowed_types" + | "unused_trait_names" ) }) { return; diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs index d5f017b26509..9952d0af99e5 100644 --- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs +++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs @@ -7,7 +7,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::CoroutineLayout; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index e933fdf1d6e6..3c2af72624f6 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -4,12 +4,12 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, UnOp}; use rustc_lint::{LateContext, LateLintPass, Level}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs index 8459f051d3d9..8261c65354fd 100644 --- a/src/tools/clippy/clippy_lints/src/box_default.rs +++ b/src/tools/clippy/clippy_lints/src/box_default.rs @@ -4,7 +4,7 @@ use clippy_utils::ty::expr_sig; use clippy_utils::{is_default_equivalent, path_def_id}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{Block, Expr, ExprKind, LetStmt, Node, QPath, Ty, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs index a9fe190f1777..dd2620b0b9df 100644 --- a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs +++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// ```ignore /// b"Hello" /// ``` - #[clippy::version = "1.68.0"] + #[clippy::version = "1.81.0"] pub BYTE_CHAR_SLICES, style, "hard to read byte char slice" diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs index e924542fea2a..ffd6c520c9ae 100644 --- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs +++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs @@ -2,7 +2,7 @@ use super::LINT_GROUPS_PRIORITY; use clippy_utils::diagnostics::span_lint_and_then; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_lint::{unerased_lint_store, LateContext}; +use rustc_lint::{LateContext, unerased_lint_store}; use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext}; use serde::{Deserialize, Serialize}; use std::collections::BTreeMap; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index 346aed7e9f11..84a44b14dde5 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -10,7 +10,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_span::hygiene; -use super::{utils, CAST_LOSSLESS}; +use super::{CAST_LOSSLESS, utils}; pub(super) fn check( cx: &LateContext<'_>, diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs index 5708aae3f3ec..40a1a9d1ce8f 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs @@ -12,7 +12,7 @@ use rustc_middle::ty::{self, FloatTy, Ty}; use rustc_span::Span; use rustc_target::abi::IntegerType; -use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; +use super::{CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION, utils}; fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option { if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs index 11274383595a..3cf4a43b0d4c 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs @@ -3,7 +3,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use super::{utils, CAST_POSSIBLE_WRAP}; +use super::{CAST_POSSIBLE_WRAP, utils}; // this should be kept in sync with the allowed bit widths of `usize` and `isize` const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64]; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs index 035666e4d4c9..1eb115ce6bdc 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_precision_loss.rs @@ -4,7 +4,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use super::{utils, CAST_PRECISION_LOSS}; +use super::{CAST_PRECISION_LOSS, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { if !cast_from.is_integral() || cast_to.is_integral() { diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs index 9daf237344a4..4be53ace6871 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint; -use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{method_chain_args, sext}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs index dbe03e4ae809..ac1a355c8d96 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, UintTy}; -use super::{utils, FN_TO_NUMERIC_CAST}; +use super::{FN_TO_NUMERIC_CAST, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { // We only want to check casts to `ty::Uint` or `ty::Int` diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs index dfbae1618ac6..18e7798452ea 100644 --- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs +++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs @@ -5,7 +5,7 @@ use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION}; +use super::{FN_TO_NUMERIC_CAST_WITH_TRUNCATION, utils}; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { // We only want to check casts to `ty::Uint` or `ty::Int` diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs index c31716fbcee1..3acd4eca420e 100644 --- a/src/tools/clippy/clippy_lints/src/casts/mod.rs +++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs @@ -23,8 +23,8 @@ mod unnecessary_cast; mod utils; mod zero_ptr; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::is_hir_ty_cfg_dependant; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -410,19 +410,27 @@ declare_clippy_lint! { /// ### Why is this bad? /// Though `as` casts between raw pointers are not terrible, `pointer::cast_mut` and /// `pointer::cast_const` are safer because they cannot accidentally cast the pointer to another - /// type. + /// type. Or, when null pointers are involved, `null()` and `null_mut()` can be used directly. /// /// ### Example /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr as *mut u32; /// let ptr = mut_ptr as *const u32; + /// let ptr1 = std::ptr::null::() as *mut u32; + /// let ptr2 = std::ptr::null_mut::() as *const u32; + /// let ptr3 = std::ptr::null::().cast_mut(); + /// let ptr4 = std::ptr::null_mut::().cast_const(); /// ``` /// Use instead: /// ```no_run /// let ptr: *const u32 = &42_u32; /// let mut_ptr = ptr.cast_mut(); /// let ptr = mut_ptr.cast_const(); + /// let ptr1 = std::ptr::null_mut::(); + /// let ptr2 = std::ptr::null::(); + /// let ptr3 = std::ptr::null_mut::(); + /// let ptr4 = std::ptr::null::(); /// ``` #[clippy::version = "1.72.0"] pub PTR_CAST_CONSTNESS, @@ -809,6 +817,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { char_lit_as_u8::check(cx, expr); ptr_as_ptr::check(cx, expr, &self.msrv); cast_slice_different_sizes::check(cx, expr, &self.msrv); + ptr_cast_constness::check_null_ptr_cast_method(cx, expr); } extract_msrv_attr!(LateContext); diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs index 7513e18d408b..7518dd2435ae 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs @@ -1,10 +1,12 @@ use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::std_or_core; use clippy_utils::sugg::Sugg; use rustc_errors::Applicability; -use rustc_hir::{Expr, Mutability}; +use rustc_hir::{Expr, ExprKind, Mutability, QPath}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; +use rustc_span::sym; use super::PTR_CAST_CONSTNESS; @@ -16,8 +18,7 @@ pub(super) fn check<'tcx>( cast_to: Ty<'tcx>, msrv: &Msrv, ) { - if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) - && let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() + if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind() && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind() && matches!( (from_mutbl, to_mutbl), @@ -26,20 +27,74 @@ pub(super) fn check<'tcx>( && from_ty == to_ty && !from_ty.has_erased_regions() { - let sugg = Sugg::hir(cx, cast_expr, "_"); - let constness = match *to_mutbl { - Mutability::Not => "const", - Mutability::Mut => "mut", - }; + if let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) { + Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")), + Some(sym::ptr_null_mut) => Some(("mutable", "const", "null")), + _ => None, + } + { + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + format!("`as` casting to make a {source} null pointer into a {target} null pointer"), + format!("use `{target_func}()` directly instead"), + format!("{prefix}::ptr::{target_func}::<{after_lt}"), + app, + ); + return; + } + if msrv.meets(msrvs::POINTER_CAST_CONSTNESS) { + let sugg = Sugg::hir(cx, cast_expr, "_"); + let constness = match *to_mutbl { + Mutability::Not => "const", + Mutability::Mut => "mut", + }; + + span_lint_and_sugg( + cx, + PTR_CAST_CONSTNESS, + expr.span, + "`as` casting between raw pointers while changing only its constness", + format!("try `pointer::cast_{constness}`, a safer alternative"), + format!("{}.cast_{constness}()", sugg.maybe_par()), + Applicability::MachineApplicable, + ); + } + } +} + +pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind + && let ExprKind::Call(func, []) = cast_expr.kind + && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind + && let Some(defid) = path.res.opt_def_id() + && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.as_str()) { + (Some(sym::ptr_null), "cast_mut") => "null_mut", + (Some(sym::ptr_null_mut), "cast_const") => "null", + _ => return, + } + && let Some(prefix) = std_or_core(cx) + && let mut app = Applicability::MachineApplicable + && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app)) + && let Some((_, after_lt)) = sugg.split_once("::<") + { span_lint_and_sugg( cx, PTR_CAST_CONSTNESS, expr.span, - "`as` casting between raw pointers while changing only its constness", - format!("try `pointer::cast_{constness}`, a safer alternative"), - format!("{}.cast_{constness}()", sugg.maybe_par()), - Applicability::MachineApplicable, + "changing constness of a null pointer", + format!("use `{method}()` directly instead"), + format!("{prefix}::ptr::{method}::<{after_lt}"), + app, ); } } diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs index 5f48b8bd2063..dfa240ccec62 100644 --- a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; -use clippy_utils::{expr_use_ctxt, is_no_std_crate, ExprUseNode}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_no_std_crate}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 566adc83d690..811d33c8bde2 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::{snippet_opt, SpanRangeExt}; -use clippy_utils::visitors::{for_each_expr_without_closures, Visitable}; +use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs index 5a4f20f09906..5ccba92a0afe 100644 --- a/src/tools/clippy/clippy_lints/src/casts/utils.rs +++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs @@ -1,4 +1,4 @@ -use clippy_utils::ty::{read_explicit_enum_value, EnumValue}; +use clippy_utils::ty::{EnumValue, read_explicit_enum_value}; use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr}; /// Returns the size in bits of an integral type. diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs index d820c1e0720b..884d15cabffc 100644 --- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs +++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs @@ -22,7 +22,7 @@ declare_clippy_lint! { /// # fn important_check() {} /// important_check(); /// ``` - #[clippy::version = "1.73.0"] + #[clippy::version = "1.81.0"] pub CFG_NOT_TEST, restriction, "enforce against excluding code from test builds" diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs index dd7c34d1e468..d3aa2fd1ea12 100644 --- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 0099eefbc8dc..495d8ce3fa70 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{get_async_fn_body, is_async_fn, LimitStack}; +use clippy_utils::{LimitStack, get_async_fn_body, is_async_fn}; use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; @@ -11,7 +11,7 @@ use rustc_hir::{Body, Expr, ExprKind, FnDecl}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index c6847411c75a..8276e53648c0 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{Visitable, for_each_expr}; use clippy_utils::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind}; diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs index 5d78744e9b5e..b9baf9af248e 100644 --- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs +++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::implements_trait; -use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq}; +use clippy_utils::{SpanlessEq, if_sequence, is_else_clause, is_in_const_context}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs index 86e0368c4e42..d90a22bb62a6 100644 --- a/src/tools/clippy/clippy_lints/src/copies.rs +++ b/src/tools/clippy/clippy_lints/src/copies.rs @@ -1,16 +1,17 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; -use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, IntoSpan, SpanRangeExt}; -use clippy_utils::ty::{needs_ordered_drop, InteriorMut}; +use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::ty::{InteriorMut, needs_ordered_drop}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ - capture_local_usage, eq_expr_value, find_binding_init, get_enclosing_block, hash_expr, hash_stmt, if_sequence, - is_else_clause, is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq, + ContainsName, HirEqInterExpr, SpanlessEq, capture_local_usage, eq_expr_value, find_binding_init, + get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed, path_to_local, + search_same, }; use core::iter; use core::ops::ControlFlow; use rustc_errors::Applicability; -use rustc_hir::{intravisit, BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind}; +use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind, intravisit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs index 678bdbc0ffb8..c8f814137289 100644 --- a/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs +++ b/src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs @@ -5,8 +5,8 @@ use rustc_ast::tokenstream::{TokenStream, TokenTree}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 93c8fff05e9e..a96c86f07657 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; -use clippy_utils::macros::{macro_backtrace, MacroCall}; +use clippy_utils::macros::{MacroCall, macro_backtrace}; use clippy_utils::source::snippet_with_applicability; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -9,7 +9,7 @@ use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 60e517131737..2b229d2fe6ac 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -49,8 +49,6 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::attrs::DEPRECATED_CLIPPY_CFG_ATTR_INFO, crate::attrs::DEPRECATED_SEMVER_INFO, crate::attrs::DUPLICATED_ATTRIBUTES_INFO, - crate::attrs::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, - crate::attrs::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::attrs::INLINE_ALWAYS_INFO, crate::attrs::MIXED_ATTRIBUTES_STYLE_INFO, crate::attrs::NON_MINIMAL_CFG_INFO, @@ -138,6 +136,8 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::doc::DOC_LINK_WITH_QUOTES_INFO, crate::doc::DOC_MARKDOWN_INFO, crate::doc::EMPTY_DOCS_INFO, + crate::doc::EMPTY_LINE_AFTER_DOC_COMMENTS_INFO, + crate::doc::EMPTY_LINE_AFTER_OUTER_ATTR_INFO, crate::doc::MISSING_ERRORS_DOC_INFO, crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO, @@ -217,6 +217,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::implicit_return::IMPLICIT_RETURN_INFO, crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO, crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO, + crate::implicit_saturating_sub::INVERTED_SATURATING_SUB_INFO, crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO, crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO, crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO, @@ -300,10 +301,12 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::manual_async_fn::MANUAL_ASYNC_FN_INFO, crate::manual_bits::MANUAL_BITS_INFO, crate::manual_clamp::MANUAL_CLAMP_INFO, + crate::manual_div_ceil::MANUAL_DIV_CEIL_INFO, crate::manual_float_methods::MANUAL_IS_FINITE_INFO, crate::manual_float_methods::MANUAL_IS_INFINITE_INFO, crate::manual_hash_one::MANUAL_HASH_ONE_INFO, crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO, + crate::manual_is_power_of_two::MANUAL_IS_POWER_OF_TWO_INFO, crate::manual_let_else::MANUAL_LET_ELSE_INFO, crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO, crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO, @@ -464,6 +467,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::methods::UNNECESSARY_FALLIBLE_CONVERSIONS_INFO, crate::methods::UNNECESSARY_FILTER_MAP_INFO, crate::methods::UNNECESSARY_FIND_MAP_INFO, + crate::methods::UNNECESSARY_FIRST_THEN_CHECK_INFO, crate::methods::UNNECESSARY_FOLD_INFO, crate::methods::UNNECESSARY_GET_THEN_CHECK_INFO, crate::methods::UNNECESSARY_JOIN_INFO, @@ -486,6 +490,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::misc::SHORT_CIRCUIT_STATEMENT_INFO, crate::misc::TOPLEVEL_REF_ARG_INFO, crate::misc::USED_UNDERSCORE_BINDING_INFO, + crate::misc::USED_UNDERSCORE_ITEMS_INFO, crate::misc_early::BUILTIN_TYPE_SHADOW_INFO, crate::misc_early::DOUBLE_NEG_INFO, crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO, @@ -553,6 +558,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::non_expressive_names::SIMILAR_NAMES_INFO, crate::non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS_INFO, crate::non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY_INFO, + crate::non_zero_suggestions::NON_ZERO_SUGGESTIONS_INFO, crate::nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES_INFO, crate::octal_escapes::OCTAL_ESCAPES_INFO, crate::only_used_in_recursion::ONLY_USED_IN_RECURSION_INFO, @@ -598,6 +604,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::pathbuf_init_then_push::PATHBUF_INIT_THEN_PUSH_INFO, crate::pattern_type_mismatch::PATTERN_TYPE_MISMATCH_INFO, crate::permissions_set_readonly_false::PERMISSIONS_SET_READONLY_FALSE_INFO, + crate::pointers_in_nomem_asm_block::POINTERS_IN_NOMEM_ASM_BLOCK_INFO, crate::precedence::PRECEDENCE_INFO, crate::ptr::CMP_NULL_INFO, crate::ptr::INVALID_NULL_PTR_USAGE_INFO, @@ -741,6 +748,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::unused_result_ok::UNUSED_RESULT_OK_INFO, crate::unused_rounding::UNUSED_ROUNDING_INFO, crate::unused_self::UNUSED_SELF_INFO, + crate::unused_trait_names::UNUSED_TRAIT_NAMES_INFO, crate::unused_unit::UNUSED_UNIT_INFO, crate::unwrap::PANICKING_UNWRAP_INFO, crate::unwrap::UNNECESSARY_UNWRAP_INFO, @@ -767,4 +775,5 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::zero_div_zero::ZERO_DIVIDED_BY_ZERO_INFO, crate::zero_repeat_side_effects::ZERO_REPEAT_SIDE_EFFECTS_INFO, crate::zero_sized_map_values::ZERO_SIZED_MAP_VALUES_INFO, + crate::zombie_processes::ZOMBIE_PROCESSES_INFO, ]; diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs index 0b7279f2b360..dc10b64698b4 100644 --- a/src/tools/clippy/clippy_lints/src/default.rs +++ b/src/tools/clippy/clippy_lints/src/default.rs @@ -11,7 +11,7 @@ use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index 137781754966..33a97222b8f8 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_ty_alias; -use hir::def::Res; use hir::ExprKind; +use hir::def::Res; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs index ac49e6f1a482..056e39c02af9 100644 --- a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs +++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs @@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::{last_path_segment, std_or_core}; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, GenericArg, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs index 05c3cd3c8140..a065dc2cf7e5 100644 --- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs +++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs @@ -3,7 +3,7 @@ use clippy_utils::numeric_literal; use clippy_utils::source::snippet_opt; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt}; use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs index 0e55d3db469a..f34f5e056064 100644 --- a/src/tools/clippy/clippy_lints/src/dereference.rs +++ b/src/tools/clippy/clippy_lints/src/dereference.rs @@ -3,14 +3,14 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{implements_trait, is_manually_drop}; use clippy_utils::{ - expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy, - ExprUseNode, + DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, + peel_middle_ty_refs, }; use core::mem; use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_ty}; use rustc_hir::{ self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TyKind, UnOp, @@ -290,13 +290,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && let Some(ty) = use_node.defined_ty(cx) && TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return()).is_deref_stable() { - self.state = Some(( - State::ExplicitDeref { mutability: None }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::ExplicitDeref { mutability: None }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, RefOp::Method { mutbl, is_ufcs } @@ -458,13 +455,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_))) { - self.state = Some(( - State::Borrow { mutability }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::Borrow { mutability }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, _ => {}, @@ -508,13 +502,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { let stability = state.stability; report(cx, expr, State::DerefedBorrow(state), data, typeck); if stability.is_deref_stable() { - self.state = Some(( - State::Borrow { mutability }, - StateData { - first_expr: expr, - adjusted_ty, - }, - )); + self.state = Some((State::Borrow { mutability }, StateData { + first_expr: expr, + adjusted_ty, + })); } }, (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => { @@ -539,13 +530,10 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { } else if stability.is_deref_stable() && let Some(parent) = get_parent_expr(cx, expr) { - self.state = Some(( - State::ExplicitDeref { mutability: None }, - StateData { - first_expr: parent, - adjusted_ty, - }, - )); + self.state = Some((State::ExplicitDeref { mutability: None }, StateData { + first_expr: parent, + adjusted_ty, + })); } }, @@ -1138,20 +1126,17 @@ impl<'tcx> Dereferencing<'tcx> { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref - if !matches!( - cx.typeck_results().expr_adjustments(e), - [ - Adjustment { - kind: Adjust::Deref(_), - .. - }, - Adjustment { - kind: Adjust::Deref(_), - .. - }, + if !matches!(cx.typeck_results().expr_adjustments(e), [ + Adjustment { + kind: Adjust::Deref(_), .. - ] - ) { + }, + Adjustment { + kind: Adjust::Deref(_), + .. + }, + .. + ]) { match get_parent_expr(cx, e) { // Field accesses are the same no matter the number of references. Some(Expr { diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs index f27f68e2cbc5..2920bbb4c81e 100644 --- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs +++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::indent_of; use clippy_utils::{is_default_equivalent, peel_blocks}; diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs index 649c480fb92a..35a97adfb452 100644 --- a/src/tools/clippy/clippy_lints/src/derive.rs +++ b/src/tools/clippy/clippy_lints/src/derive.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths}; 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::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item}; use rustc_hir::{ self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource, }; @@ -15,7 +15,7 @@ use rustc_middle::ty::{ }; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs new file mode 100644 index 000000000000..de7a2c2433f4 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/doc/empty_line_after.rs @@ -0,0 +1,342 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::{SpanRangeExt, snippet_indent}; +use clippy_utils::tokenize_with_text; +use itertools::Itertools; +use rustc_ast::token::CommentKind; +use rustc_ast::{AttrKind, AttrStyle, Attribute}; +use rustc_errors::{Applicability, Diag, SuggestionStyle}; +use rustc_hir::{ItemKind, Node}; +use rustc_lexer::TokenKind; +use rustc_lint::LateContext; +use rustc_span::{BytePos, ExpnKind, InnerSpan, Span, SpanData}; + +use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR}; + +#[derive(Debug, PartialEq, Clone, Copy)] +enum StopKind { + Attr, + Doc(CommentKind), +} + +impl StopKind { + fn is_doc(self) -> bool { + matches!(self, StopKind::Doc(_)) + } +} + +#[derive(Debug)] +struct Stop { + span: Span, + kind: StopKind, + first: usize, + last: usize, +} + +impl Stop { + fn convert_to_inner(&self) -> (Span, String) { + let inner = match self.kind { + // #|[...] + StopKind::Attr => InnerSpan::new(1, 1), + // /// or /** + // ^ ^ + StopKind::Doc(_) => InnerSpan::new(2, 3), + }; + (self.span.from_inner(inner), "!".into()) + } + + fn comment_out(&self, cx: &LateContext<'_>, suggestions: &mut Vec<(Span, String)>) { + match self.kind { + StopKind::Attr => { + if cx.tcx.sess.source_map().is_multiline(self.span) { + suggestions.extend([ + (self.span.shrink_to_lo(), "/* ".into()), + (self.span.shrink_to_hi(), " */".into()), + ]); + } else { + suggestions.push((self.span.shrink_to_lo(), "// ".into())); + } + }, + StopKind::Doc(CommentKind::Line) => suggestions.push((self.span.shrink_to_lo(), "// ".into())), + StopKind::Doc(CommentKind::Block) => { + // /** outer */ /*! inner */ + // ^ ^ + let asterisk = self.span.from_inner(InnerSpan::new(1, 2)); + suggestions.push((asterisk, String::new())); + }, + } + } + + fn from_attr(cx: &LateContext<'_>, attr: &Attribute) -> Option { + let SpanData { lo, hi, .. } = attr.span.data(); + let file = cx.tcx.sess.source_map().lookup_source_file(lo); + + Some(Self { + span: attr.span, + kind: match attr.kind { + AttrKind::Normal(_) => StopKind::Attr, + AttrKind::DocComment(comment_kind, _) => StopKind::Doc(comment_kind), + }, + first: file.lookup_line(file.relative_position(lo))?, + last: file.lookup_line(file.relative_position(hi))?, + }) + } +} + +/// Represents a set of attrs/doc comments separated by 1 or more empty lines +/// +/// ```ignore +/// /// chunk 1 docs +/// // not an empty line so also part of chunk 1 +/// #[chunk_1_attrs] // <-- prev_stop +/// +/// /* gap */ +/// +/// /// chunk 2 docs // <-- next_stop +/// #[chunk_2_attrs] +/// ``` +struct Gap<'a> { + /// The span of individual empty lines including the newline at the end of the line + empty_lines: Vec, + has_comment: bool, + next_stop: &'a Stop, + prev_stop: &'a Stop, + /// The chunk that includes [`prev_stop`](Self::prev_stop) + prev_chunk: &'a [Stop], +} + +impl<'a> Gap<'a> { + fn new(cx: &LateContext<'_>, prev_chunk: &'a [Stop], next_chunk: &'a [Stop]) -> Option { + let prev_stop = prev_chunk.last()?; + let next_stop = next_chunk.first()?; + let gap_span = prev_stop.span.between(next_stop.span); + let gap_snippet = gap_span.get_source_text(cx)?; + + let mut has_comment = false; + let mut empty_lines = Vec::new(); + + for (token, source, inner_span) in tokenize_with_text(&gap_snippet) { + match token { + TokenKind::BlockComment { + doc_style: None, + terminated: true, + } + | TokenKind::LineComment { doc_style: None } => has_comment = true, + TokenKind::Whitespace => { + let newlines = source.bytes().positions(|b| b == b'\n'); + empty_lines.extend( + newlines + .tuple_windows() + .map(|(a, b)| InnerSpan::new(inner_span.start + a + 1, inner_span.start + b)) + .map(|inner_span| gap_span.from_inner(inner_span)), + ); + }, + // Ignore cfg_attr'd out attributes as they may contain empty lines, could also be from macro + // shenanigans + _ => return None, + } + } + + (!empty_lines.is_empty()).then_some(Self { + empty_lines, + has_comment, + next_stop, + prev_stop, + prev_chunk, + }) + } + + fn contiguous_empty_lines(&self) -> impl Iterator + '_ { + self.empty_lines + // The `+ BytePos(1)` means "next line", because each empty line span is "N:1-N:1". + .chunk_by(|a, b| a.hi() + BytePos(1) == b.lo()) + .map(|chunk| { + let first = chunk.first().expect("at least one empty line"); + let last = chunk.last().expect("at least one empty line"); + // The BytePos subtraction here is safe, as before an empty line, there must be at least one + // attribute/comment. The span needs to start at the end of the previous line. + first.with_lo(first.lo() - BytePos(1)).with_hi(last.hi()) + }) + } +} + +/// If the node the attributes/docs apply to is the first in the module/crate suggest converting +/// them to inner attributes/docs +fn suggest_inner(cx: &LateContext<'_>, diag: &mut Diag<'_, ()>, kind: StopKind, gaps: &[Gap<'_>]) { + let Some(owner) = cx.last_node_with_lint_attrs.as_owner() else { + return; + }; + let parent_desc = match cx.tcx.parent_hir_node(owner.into()) { + Node::Item(item) + if let ItemKind::Mod(parent_mod) = item.kind + && let [first, ..] = parent_mod.item_ids + && first.owner_id == owner => + { + "parent module" + }, + Node::Crate(crate_mod) + if let Some(first) = crate_mod + .item_ids + .iter() + .map(|&id| cx.tcx.hir().item(id)) + // skip prelude imports + .find(|item| !matches!(item.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_))) + && first.owner_id == owner => + { + "crate" + }, + _ => return, + }; + + diag.multipart_suggestion_verbose( + match kind { + StopKind::Attr => format!("if the attribute should apply to the {parent_desc} use an inner attribute"), + StopKind::Doc(_) => format!("if the comment should document the {parent_desc} use an inner doc comment"), + }, + gaps.iter() + .flat_map(|gap| gap.prev_chunk) + .map(Stop::convert_to_inner) + .collect(), + Applicability::MaybeIncorrect, + ); +} + +fn check_gaps(cx: &LateContext<'_>, gaps: &[Gap<'_>]) -> bool { + let Some(first_gap) = gaps.first() else { + return false; + }; + let empty_lines = || gaps.iter().flat_map(|gap| gap.empty_lines.iter().copied()); + let contiguous_empty_lines = || gaps.iter().flat_map(Gap::contiguous_empty_lines); + let mut has_comment = false; + let mut has_attr = false; + for gap in gaps { + has_comment |= gap.has_comment; + if !has_attr { + has_attr = gap.prev_chunk.iter().any(|stop| stop.kind == StopKind::Attr); + } + } + let kind = first_gap.prev_stop.kind; + let (lint, kind_desc) = match kind { + StopKind::Attr => (EMPTY_LINE_AFTER_OUTER_ATTR, "outer attribute"), + StopKind::Doc(_) => (EMPTY_LINE_AFTER_DOC_COMMENTS, "doc comment"), + }; + let (lines, are, them) = if empty_lines().nth(1).is_some() { + ("lines", "are", "them") + } else { + ("line", "is", "it") + }; + span_lint_and_then( + cx, + lint, + first_gap.prev_stop.span.to(empty_lines().last().unwrap()), + format!("empty {lines} after {kind_desc}"), + |diag| { + if let Some(owner) = cx.last_node_with_lint_attrs.as_owner() { + let def_id = owner.to_def_id(); + let def_descr = cx.tcx.def_descr(def_id); + diag.span_label(cx.tcx.def_span(def_id), match kind { + StopKind::Attr => format!("the attribute applies to this {def_descr}"), + StopKind::Doc(_) => format!("the comment documents this {def_descr}"), + }); + } + + diag.multipart_suggestion_with_style( + format!("if the empty {lines} {are} unintentional remove {them}"), + contiguous_empty_lines() + .map(|empty_lines| (empty_lines, String::new())) + .collect(), + Applicability::MaybeIncorrect, + SuggestionStyle::HideCodeAlways, + ); + + if has_comment && kind.is_doc() { + // Likely doc comments that applied to some now commented out code + // + // /// Old docs for Foo + // // struct Foo; + + let mut suggestions = Vec::new(); + for stop in gaps.iter().flat_map(|gap| gap.prev_chunk) { + stop.comment_out(cx, &mut suggestions); + } + let name = match cx.tcx.hir().opt_name(cx.last_node_with_lint_attrs) { + Some(name) => format!("`{name}`"), + None => "this".into(), + }; + diag.multipart_suggestion_verbose( + format!("if the doc comment should not document {name} comment it out"), + suggestions, + Applicability::MaybeIncorrect, + ); + } else { + suggest_inner(cx, diag, kind, gaps); + } + + if kind == StopKind::Doc(CommentKind::Line) + && gaps + .iter() + .all(|gap| !gap.has_comment && gap.next_stop.kind == StopKind::Doc(CommentKind::Line)) + { + // Commentless empty gaps between line doc comments, possibly intended to be part of the markdown + + let indent = snippet_indent(cx, first_gap.prev_stop.span).unwrap_or_default(); + diag.multipart_suggestion_verbose( + format!("if the documentation should include the empty {lines} include {them} in the comment"), + empty_lines() + .map(|empty_line| (empty_line, format!("{indent}///"))) + .collect(), + Applicability::MaybeIncorrect, + ); + } + }, + ); + kind.is_doc() +} + +/// Returns `true` if [`EMPTY_LINE_AFTER_DOC_COMMENTS`] triggered, used to skip other doc comment +/// lints where they would be confusing +/// +/// [`EMPTY_LINE_AFTER_OUTER_ATTR`] is also here to share an implementation but does not return +/// `true` if it triggers +pub(super) fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool { + let mut outer = attrs + .iter() + .filter(|attr| attr.style == AttrStyle::Outer && !attr.span.from_expansion()) + .map(|attr| Stop::from_attr(cx, attr)) + .collect::>>() + .unwrap_or_default(); + + if outer.is_empty() { + return false; + } + + // Push a fake attribute Stop for the item itself so we check for gaps between the last outer + // attr/doc comment and the item they apply to + let span = cx.tcx.hir().span(cx.last_node_with_lint_attrs); + if !span.from_expansion() + && let Ok(line) = cx.tcx.sess.source_map().lookup_line(span.lo()) + { + outer.push(Stop { + span, + kind: StopKind::Attr, + first: line.line, + // last doesn't need to be accurate here, we don't compare it with anything + last: line.line, + }); + } + + let mut gaps = Vec::new(); + let mut last = 0; + for pos in outer + .array_windows() + .positions(|[a, b]| b.first.saturating_sub(a.last) > 1) + { + // we want to be after the first stop in the window + let pos = pos + 1; + if let Some(gap) = Gap::new(cx, &outer[last..pos], &outer[pos..]) { + last = pos; + gaps.push(gap); + } + } + + check_gaps(cx, &gaps) +} diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs index 771bcac24418..f9e4a43c0e7a 100644 --- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs +++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs @@ -22,7 +22,6 @@ pub(super) fn check( range: Range, mut span: Span, containers: &[super::Container], - line_break_span: Span, ) { if doc[range.clone()].contains('\t') { // We don't do tab stops correctly. @@ -52,29 +51,6 @@ pub(super) fn check( "doc list item without indentation" }; span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| { - let snippet = clippy_utils::source::snippet(cx, line_break_span, ""); - if snippet.chars().filter(|&c| c == '\n').count() > 1 - && let Some(doc_comment_start) = snippet.rfind('\n') - && let doc_comment = snippet[doc_comment_start..].trim() - && (doc_comment == "///" || doc_comment == "//!") - { - // suggest filling in a blank line - diag.span_suggestion_verbose( - line_break_span.shrink_to_lo(), - "if this should be its own paragraph, add a blank doc comment line", - format!("\n{doc_comment}"), - Applicability::MaybeIncorrect, - ); - if ccount > 0 || blockquote_level > 0 { - diag.help("if this not intended to be a quote at all, escape it with `\\>`"); - } else { - let indent = list_indentation - lcount; - diag.help(format!( - "if this is intended to be part of the list, indent {indent} spaces" - )); - } - return; - } if ccount == 0 && blockquote_level == 0 { // simpler suggestion style for indentation let indent = list_indentation - lcount; diff --git a/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs b/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs index 01191e811b00..1d4345e4541d 100644 --- a/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs +++ b/src/tools/clippy/clippy_lints/src/doc/link_with_quotes.rs @@ -3,7 +3,7 @@ use std::ops::Range; use clippy_utils::diagnostics::span_lint; use rustc_lint::LateContext; -use super::{Fragments, DOC_LINK_WITH_QUOTES}; +use super::{DOC_LINK_WITH_QUOTES, Fragments}; pub fn check(cx: &LateContext<'_>, trimmed_text: &str, range: Range, fragments: Fragments<'_>) { if ((trimmed_text.starts_with('\'') && trimmed_text.ends_with('\'')) diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index e3d748407261..c9173030a55e 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_doc_hidden, return_ty}; use rustc_hir::{BodyId, FnSig, OwnerId, Safety}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub fn check( cx: &LateContext<'_>, diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 790579b21c90..0ae1fad56921 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -23,15 +23,16 @@ use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_resolve::rustdoc::{ - add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, span_of_fragments, - DocFragment, + DocFragment, add_doc_fragment, attrs_to_doc_fragments, main_body_opts, source_span_for_markdown_range, + span_of_fragments, }; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::ops::Range; use url::Url; +mod empty_line_after; mod link_with_quotes; mod markdown; mod missing_headers; @@ -449,13 +450,88 @@ declare_clippy_lint! { /// /// and probably spanning a many rows. /// struct Foo {} /// ``` - #[clippy::version = "1.81.0"] + #[clippy::version = "1.82.0"] pub TOO_LONG_FIRST_DOC_PARAGRAPH, style, "ensure that the first line of a documentation paragraph isn't too long" } -#[derive(Clone)] +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after outer attributes + /// + /// ### Why is this bad? + /// The attribute may have meant to be an inner attribute (`#![attr]`). If + /// it was meant to be an outer attribute (`#[attr]`) then the empty line + /// should be removed + /// + /// ### Example + /// ```no_run + /// #[allow(dead_code)] + /// + /// fn not_quite_good_code() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// // Good (as inner attribute) + /// #![allow(dead_code)] + /// + /// fn this_is_fine() {} + /// + /// // or + /// + /// // Good (as outer attribute) + /// #[allow(dead_code)] + /// fn this_is_fine_too() {} + /// ``` + #[clippy::version = "pre 1.29.0"] + pub EMPTY_LINE_AFTER_OUTER_ATTR, + suspicious, + "empty line after outer attribute" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for empty lines after doc comments. + /// + /// ### Why is this bad? + /// The doc comment may have meant to be an inner doc comment, regular + /// comment or applied to some old code that is now commented out. If it was + /// intended to be a doc comment, then the empty line should be removed. + /// + /// ### Example + /// ```no_run + /// /// Some doc comment with a blank line after it. + /// + /// fn f() {} + /// + /// /// Docs for `old_code` + /// // fn old_code() {} + /// + /// fn new_code() {} + /// ``` + /// + /// Use instead: + /// ```no_run + /// //! Convert it to an inner doc comment + /// + /// // Or a regular comment + /// + /// /// Or remove the empty line + /// fn f() {} + /// + /// // /// Docs for `old_code` + /// // fn old_code() {} + /// + /// fn new_code() {} + /// ``` + #[clippy::version = "1.70.0"] + pub EMPTY_LINE_AFTER_DOC_COMMENTS, + suspicious, + "empty line after doc comments" +} + pub struct Documentation { valid_idents: FxHashSet, check_private_items: bool, @@ -482,6 +558,8 @@ impl_lint_pass!(Documentation => [ SUSPICIOUS_DOC_COMMENTS, EMPTY_DOCS, DOC_LAZY_CONTINUATION, + EMPTY_LINE_AFTER_OUTER_ATTR, + EMPTY_LINE_AFTER_DOC_COMMENTS, TOO_LONG_FIRST_DOC_PARAGRAPH, ]); @@ -612,12 +690,10 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ Some(("fake".into(), "fake".into())) } - if is_doc_hidden(attrs) { + if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) { return None; } - suspicious_doc_comments::check(cx, attrs); - let (fragments, _) = attrs_to_doc_fragments( attrs.iter().filter_map(|attr| { if in_external_macro(cx.sess(), attr.span) { @@ -816,7 +892,6 @@ fn check_doc<'a, Events: Iterator, Range, attrs: &[Attribute]) { +pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) -> bool { let replacements: Vec<_> = collect_doc_replacements(attrs); if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) { @@ -24,6 +24,10 @@ pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) { ); }, ); + + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs index 7bb3bb12f2ca..0165d24c7df2 100644 --- a/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/clippy_lints/src/doc/too_long_first_doc_paragraph.rs @@ -79,10 +79,17 @@ pub(super) fn check( && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi()) && let Some(snippet) = snippet_opt(cx, new_span) { + let Some(first) = snippet_opt(cx, first_span) else { + return; + }; + let Some(comment_form) = first.get(..3) else { + return; + }; + diag.span_suggestion( new_span, "add an empty line", - format!("{snippet}///\n"), + format!("{snippet}{comment_form}{snippet}"), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index 9c5100a8c1af..70524e458c78 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context}; use clippy_utils::{ - can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, - peel_hir_expr_while, SpanlessEq, + SpanlessEq, can_move_expr_to_closure_no_visit, higher, is_expr_final_block_expr, is_expr_used_or_unified, + peel_hir_expr_while, }; use core::fmt::{self, Write}; use rustc_errors::Applicability; use rustc_hir::hir_id::HirIdSet; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, SyntaxContext, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs index 4755cefe784c..e9e9d00907ea 100644 --- a/src/tools/clippy/clippy_lints/src/enum_clike.rs +++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{mir_to_const, Constant}; +use clippy_utils::consts::{Constant, mir_to_const}; use clippy_utils::diagnostics::span_lint; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs index 803606979410..5588124e791e 100644 --- a/src/tools/clippy/clippy_lints/src/escape.rs +++ b/src/tools/clippy/clippy_lints/src/escape.rs @@ -1,15 +1,15 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir; -use rustc_hir::{intravisit, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind}; +use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, TraitRef, Ty, TyCtxt}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::Span; use rustc_target::spec::abi::Abi; pub struct BoxedLocal { diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs index 8f469efb1b50..c88fb50b5afc 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs @@ -5,8 +5,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, Item, ItemKind, TraitFn, TraitItem, TraitItemKind, Ty}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs index 5154edd4399b..ce0e0faa0148 100644 --- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs +++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; use rustc_ast::node_id::NodeSet; -use rustc_ast::visit::{walk_block, walk_item, Visitor}; +use rustc_ast::visit::{Visitor, walk_block, walk_item}; use rustc_ast::{Block, Crate, Inline, Item, ItemKind, ModKind, NodeId}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs index 3c4a043a732b..5b423a96918f 100644 --- a/src/tools/clippy/clippy_lints/src/explicit_write.rs +++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_expn_of, path_def_id}; use rustc_errors::Applicability; @@ -7,7 +7,7 @@ use rustc_hir::def::Res; use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, ExpnId}; +use rustc_span::{ExpnId, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index bfe4e253ae47..bf9388b4a70f 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::{is_from_proc_macro, trait_ref_of_method}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_impl_item, walk_item, walk_param_bound, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty}; use rustc_hir::{ BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind, PredicateOrigin, Ty, TyKind, WherePredicate, @@ -12,8 +12,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::Span; +use rustc_span::def_id::{DefId, LocalDefId}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs index 0446943321aa..747ea9a43446 100644 --- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs +++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs @@ -6,7 +6,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs index 95b8e882da79..ba2b37fbf11a 100644 --- a/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs +++ b/src/tools/clippy/clippy_lints/src/field_scoped_visibility_modifiers.rs @@ -41,7 +41,7 @@ declare_clippy_lint! { /// } /// } /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub FIELD_SCOPED_VISIBILITY_MODIFIERS, restriction, "checks for usage of a scoped visibility modifier, like `pub(crate)`, on fields" diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs index bf4bcabfe891..8da6623f34de 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::Constant::{Int, F32, F64}; +use clippy_utils::consts::Constant::{F32, F64, Int}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{ diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 4dedff69b9ab..5e3f6b6a1370 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, root_macro_call_first_node}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 020c0c5440d9..83ab9f6557bd 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -1,11 +1,12 @@ use arrayvec::ArrayVec; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::is_diag_trait_item; use clippy_utils::macros::{ - find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro, - is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall, + FormatArgsStorage, FormatParamUsage, MacroCall, find_format_arg_expr, format_arg_removal_span, + format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call, + root_macro_call_first_node, }; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{implements_trait, is_type_lang_item}; @@ -18,11 +19,11 @@ use rustc_errors::Applicability; use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; +use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs index 7cd12ac7db85..c196f404ce67 100644 --- a/src/tools/clippy/clippy_lints/src/format_impl.rs +++ b/src/tools/clippy/clippy_lints/src/format_impl.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; -use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, is_format_macro, root_macro_call_first_node}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_errors::Applicability; @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index c6f03c3a7cf9..8b1f86cbb913 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::is_type_lang_item; use clippy_utils::higher; +use clippy_utils::ty::is_type_lang_item; use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index b84bf7c821ce..8832cd42dd98 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -1,11 +1,11 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::path_def_id; use clippy_utils::source::SpanRangeExt; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path, PathSegment, Ty, TyKind, diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs index e5fa67d69642..7361546153c2 100644 --- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs +++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs @@ -3,7 +3,7 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{is_in_const_context, is_integer_literal}; use rustc_errors::Applicability; -use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; +use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs index 0f48941783b2..ba8a459c917e 100644 --- a/src/tools/clippy/clippy_lints/src/functions/mod.rs +++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs @@ -14,8 +14,8 @@ use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefIdSet, LocalDefId}; use rustc_span::Span; +use rustc_span::def_id::{DefIdSet, LocalDefId}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 938281210470..cfd11e9339fb 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::attrs::is_proc_macro; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index 1aeefe73cf68..c2b40ae01d4c 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -1,4 +1,4 @@ -use rustc_hir::{self as hir, intravisit, HirId, HirIdSet}; +use rustc_hir::{self as hir, HirId, HirIdSet, intravisit}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::def_id::LocalDefId; diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs index c7de0385c021..ac2e866e4ff7 100644 --- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs +++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs @@ -4,8 +4,8 @@ use rustc_hir::def_id::{DefId, DefIdSet}; use rustc_hir::hir_id::OwnerId; use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef}; use rustc_lint::LateContext; -use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, kw}; use super::RENAMED_FUNCTION_PARAMS; diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs index c3a0b40a677a..d4eaa1663208 100644 --- a/src/tools/clippy/clippy_lints/src/functions/result.rs +++ b/src/tools/clippy/clippy_lints/src/functions/result.rs @@ -3,11 +3,11 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then}; use clippy_utils::trait_ref_of_method; -use clippy_utils::ty::{approx_ty_size, is_type_diagnostic_item, AdtVariantInfo}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_type_diagnostic_item}; use super::{RESULT_LARGE_ERR, RESULT_UNIT_ERR}; diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs index 9488ba756865..a4dbe134f365 100644 --- a/src/tools/clippy/clippy_lints/src/future_not_send.rs +++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs @@ -8,7 +8,7 @@ use rustc_middle::ty::print::PrintTraitRefExt; use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt}; diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs index 0bca53c1536d..d63c18c0edaa 100644 --- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs +++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::source::snippet_with_context; @@ -105,6 +105,8 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app); let closure = if method_name == "then" { "|| " } else { "" }; format!("{closure} {{ {block_snippet}; {arg_snip} }}") + } else if method_name == "then" { + (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned() } else { arg_snip.into_owned() }; diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index e56f33f8dcfe..f683925145a2 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -3,18 +3,18 @@ use std::collections::BTreeMap; use rustc_errors::{Applicability, Diag}; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_body, walk_expr, walk_inf, walk_ty}; use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; use clippy_utils::ty::is_type_diagnostic_item; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index 127c73ed637b..f4a64f5c20b4 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -1,11 +1,17 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::source::snippet_opt; +use clippy_utils::{ + SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt, +}; use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -39,7 +45,49 @@ declare_clippy_lint! { "Perform saturating subtraction instead of implicitly checking lower bound of data type" } -declare_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB]); +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons between integers, followed by subtracting the greater value from the + /// lower one. + /// + /// ### Why is this bad? + /// This could result in an underflow and is most likely not what the user wants. If this was + /// intended to be a saturated subtraction, consider using the `saturating_sub` method directly. + /// + /// ### Example + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = if a > b { b - a } else { 0 }; + /// ``` + /// + /// Use instead: + /// ```no_run + /// let a = 12u32; + /// let b = 13u32; + /// + /// let result = a.saturating_sub(b); + /// ``` + #[clippy::version = "1.44.0"] + pub INVERTED_SATURATING_SUB, + correctness, + "Check if a variable is smaller than another one and still subtract from it even if smaller" +} + +pub struct ImplicitSaturatingSub { + msrv: Msrv, +} + +impl_lint_pass!(ImplicitSaturatingSub => [IMPLICIT_SATURATING_SUB, INVERTED_SATURATING_SUB]); + +impl ImplicitSaturatingSub { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { @@ -50,73 +98,260 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind - - // Ensure that the binary operator is >, !=, or < - && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node) - - // Check if assign operation is done - && let Some(target) = subtracts_one(cx, then) - - // Extracting out the variable name - && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind { - // Handle symmetric conditions in the if statement - let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { - if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_left, cond_right) - } else { - return; - } - } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { - if BinOpKind::Lt == cond_op.node || BinOpKind::Ne == cond_op.node { - (cond_right, cond_left) - } else { - return; - } + check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then); + } else if let Some(higher::If { + cond, + then: if_block, + r#else: Some(else_block), + }) = higher::If::hir(expr) + && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind + { + check_manual_check( + cx, expr, cond_op, cond_left, cond_right, if_block, else_block, &self.msrv, + ); + } + } + + extract_msrv_attr!(LateContext); +} + +#[allow(clippy::too_many_arguments)] +fn check_manual_check<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + condition: &BinOp, + left_hand: &Expr<'tcx>, + right_hand: &Expr<'tcx>, + if_block: &Expr<'tcx>, + else_block: &Expr<'tcx>, + msrv: &Msrv, +) { + let ty = cx.typeck_results().expr_ty(left_hand); + if ty.is_numeric() && !ty.is_signed() { + match condition.node { + BinOpKind::Gt | BinOpKind::Ge => check_gt( + cx, + condition.span, + expr.span, + left_hand, + right_hand, + if_block, + else_block, + msrv, + ), + BinOpKind::Lt | BinOpKind::Le => check_gt( + cx, + condition.span, + expr.span, + right_hand, + left_hand, + if_block, + else_block, + msrv, + ), + _ => {}, + } + } +} + +#[allow(clippy::too_many_arguments)] +fn check_gt( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: &Expr<'_>, + little_var: &Expr<'_>, + if_block: &Expr<'_>, + else_block: &Expr<'_>, + msrv: &Msrv, +) { + if let Some(big_var) = Var::new(big_var) + && let Some(little_var) = Var::new(little_var) + { + check_subtraction( + cx, + condition_span, + expr_span, + big_var, + little_var, + if_block, + else_block, + msrv, + ); + } +} + +struct Var { + span: Span, + hir_id: HirId, +} + +impl Var { + fn new(expr: &Expr<'_>) -> Option { + path_to_local(expr).map(|hir_id| Self { + span: expr.span, + hir_id, + }) + } +} + +#[allow(clippy::too_many_arguments)] +fn check_subtraction( + cx: &LateContext<'_>, + condition_span: Span, + expr_span: Span, + big_var: Var, + little_var: Var, + if_block: &Expr<'_>, + else_block: &Expr<'_>, + msrv: &Msrv, +) { + let if_block = peel_blocks(if_block); + let else_block = peel_blocks(else_block); + if is_integer_literal(if_block, 0) { + // We need to check this case as well to prevent infinite recursion. + if is_integer_literal(else_block, 0) { + // Well, seems weird but who knows? + return; + } + // If the subtraction is done in the `else` block, then we need to also revert the two + // variables as it means that the check was reverted too. + check_subtraction( + cx, + condition_span, + expr_span, + little_var, + big_var, + else_block, + if_block, + msrv, + ); + return; + } + if is_integer_literal(else_block, 0) + && let ExprKind::Binary(op, left, right) = if_block.kind + && let BinOpKind::Sub = op.node + { + let local_left = path_to_local(left); + let local_right = path_to_local(right); + if Some(big_var.hir_id) == local_left && Some(little_var.hir_id) == local_right { + // This part of the condition is voluntarily split from the one before to ensure that + // if `snippet_opt` fails, it won't try the next conditions. + if let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + && (!is_in_const_context(cx) || msrv.meets(msrvs::SATURATING_SUB_CONST)) + { + span_lint_and_sugg( + cx, + IMPLICIT_SATURATING_SUB, + expr_span, + "manual arithmetic check found", + "replace it with", + format!("{big_var_snippet}.saturating_sub({little_var_snippet})"), + Applicability::MachineApplicable, + ); + } + } else if Some(little_var.hir_id) == local_left + && Some(big_var.hir_id) == local_right + && let Some(big_var_snippet) = snippet_opt(cx, big_var.span) + && let Some(little_var_snippet) = snippet_opt(cx, little_var.span) + { + span_lint_and_then( + cx, + INVERTED_SATURATING_SUB, + condition_span, + "inverted arithmetic check before subtraction", + |diag| { + diag.span_note( + if_block.span, + format!("this subtraction underflows when `{little_var_snippet} < {big_var_snippet}`"), + ); + diag.span_suggestion( + if_block.span, + "try replacing it with", + format!("{big_var_snippet} - {little_var_snippet}"), + Applicability::MaybeIncorrect, + ); + }, + ); + } + } +} + +fn check_with_condition<'tcx>( + cx: &LateContext<'tcx>, + expr: &Expr<'tcx>, + cond_op: BinOpKind, + cond_left: &Expr<'tcx>, + cond_right: &Expr<'tcx>, + then: &Expr<'tcx>, +) { + // Ensure that the binary operator is >, !=, or < + if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op) + + // Check if assign operation is done + && let Some(target) = subtracts_one(cx, then) + + // Extracting out the variable name + && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind + { + // Handle symmetric conditions in the if statement + let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { + if BinOpKind::Gt == cond_op || BinOpKind::Ne == cond_op { + (cond_left, cond_right) } else { return; - }; - - // Check if the variable in the condition statement is an integer - if !cx.typeck_results().expr_ty(cond_var).is_integral() { + } + } else if SpanlessEq::new(cx).eq_expr(cond_right, target) { + if BinOpKind::Lt == cond_op || BinOpKind::Ne == cond_op { + (cond_right, cond_left) + } else { return; } + } else { + return; + }; - // Get the variable name - let var_name = ares_path.segments[0].ident.name.as_str(); - match cond_num_val.kind { - ExprKind::Lit(cond_lit) => { - // Check if the constant is zero - if let LitKind::Int(Pu128(0), _) = cond_lit.node { - if cx.typeck_results().expr_ty(cond_left).is_signed() { - } else { - print_lint_and_sugg(cx, var_name, expr); - }; - } - }, - ExprKind::Path(QPath::TypeRelative(_, name)) => { - if name.ident.as_str() == "MIN" - && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(const_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { + // Check if the variable in the condition statement is an integer + if !cx.typeck_results().expr_ty(cond_var).is_integral() { + return; + } + + // Get the variable name + let var_name = ares_path.segments[0].ident.name.as_str(); + match cond_num_val.kind { + ExprKind::Lit(cond_lit) => { + // Check if the constant is zero + if let LitKind::Int(Pu128(0), _) = cond_lit.node { + if cx.typeck_results().expr_ty(cond_left).is_signed() { + } else { print_lint_and_sugg(cx, var_name, expr); - } - }, - ExprKind::Call(func, []) => { - if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind - && name.ident.as_str() == "min_value" - && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) - && let Some(impl_id) = cx.tcx.impl_of_method(func_id) - && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl - && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() - { - print_lint_and_sugg(cx, var_name, expr); - } - }, - _ => (), - } + }; + } + }, + ExprKind::Path(QPath::TypeRelative(_, name)) => { + if name.ident.as_str() == "MIN" + && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(const_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + ExprKind::Call(func, []) => { + if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind + && name.ident.as_str() == "min_value" + && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id) + && let Some(impl_id) = cx.tcx.impl_of_method(func_id) + && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl + && cx.tcx.type_of(impl_id).instantiate_identity().is_integral() + { + print_lint_and_sugg(cx, var_name, expr); + } + }, + _ => (), } } } diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 2ad045e12686..0b3a6ee1beae 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint; use clippy_utils::is_in_test; use rustc_attr::{StabilityLevel, StableSince}; @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir::{Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; -use rustc_session::{impl_lint_pass, RustcVersion}; +use rustc_session::{RustcVersion, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::{ExpnKind, Span}; diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs index 2f9661c9ea38..39afb6810b8e 100644 --- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs +++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; @@ -8,14 +8,14 @@ use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::HirId; +use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index af5b1f957399..3a28553b55e2 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs index 6d6820311b67..66931a7f98c3 100644 --- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; use clippy_utils::sugg::Sugg; diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 74bf82f58bdc..66a8a3167a4f 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -8,8 +8,8 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index 8caa484bb993..1ac549b74ac6 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -6,7 +6,7 @@ use rustc_hir::{HirId, Item, ItemKind, Mod}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::hygiene::AstPass; -use rustc_span::{sym, ExpnKind}; +use rustc_span::{ExpnKind, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs index ba0cd5d6eb35..b19b348c7430 100644 --- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs @@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs index f162948bb445..5131f5b7269b 100644 --- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs +++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs @@ -55,7 +55,9 @@ impl LateLintPass<'_> for IterOverHashType { if let Some(for_loop) = ForLoop::hir(expr) && !for_loop.body.span.from_expansion() && let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs() - && hash_iter_tys.into_iter().any(|sym| is_type_diagnostic_item(cx, ty, sym)) + && hash_iter_tys + .into_iter() + .any(|sym| is_type_diagnostic_item(cx, ty, sym)) { span_lint( cx, diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs index 2f027c117072..923089c72232 100644 --- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs +++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::ty::{approx_ty_size, is_copy, AdtVariantInfo}; +use clippy_utils::ty::{AdtVariantInfo, approx_ty_size, is_copy}; use rustc_errors::Applicability; use rustc_hir::{Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs index 15a75b060896..0f061d6de504 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_arrays.rs @@ -8,7 +8,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, ConstKind}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index ccab1e27d3b0..e17d6213679a 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; use clippy_config::Conf; +use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::{get_parent_expr, is_from_proc_macro}; use hir::def_id::DefId; @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 16f86b18e2e5..8bc2a56af993 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; -use clippy_utils::sugg::{has_enclosing_paren, Sugg}; -use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::sugg::{Sugg, has_enclosing_paren}; +use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, is_trait_method, peel_ref_operators}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -185,6 +185,19 @@ impl<'tcx> LateLintPass<'tcx> for LenZero { ); } + if let ExprKind::MethodCall(method, lhs_expr, [rhs_expr], _) = expr.kind + && is_trait_method(cx, expr, sym::PartialEq) + && !expr.span.from_expansion() + { + check_empty_expr( + cx, + expr.span, + lhs_expr, + peel_ref_operators(cx, rhs_expr), + (method.ident.name == sym::ne).then_some("!").unwrap_or_default(), + ); + } + if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind && !expr.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 2ac06b360bea..1d41f568f378 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -203,9 +203,11 @@ mod manual_assert; mod manual_async_fn; mod manual_bits; mod manual_clamp; +mod manual_div_ceil; mod manual_float_methods; mod manual_hash_one; mod manual_is_ascii_check; +mod manual_is_power_of_two; mod manual_let_else; mod manual_main_separator_str; mod manual_non_exhaustive; @@ -271,6 +273,7 @@ mod non_copy_const; mod non_expressive_names; mod non_octal_unix_permissions; mod non_send_fields_in_send_ty; +mod non_zero_suggestions; mod nonstandard_macro_braces; mod octal_escapes; mod only_used_in_recursion; @@ -287,6 +290,7 @@ mod pass_by_ref_or_value; mod pathbuf_init_then_push; mod pattern_type_mismatch; mod permissions_set_readonly_false; +mod pointers_in_nomem_asm_block; mod precedence; mod ptr; mod ptr_offset_with_cast; @@ -372,6 +376,7 @@ mod unused_peekable; mod unused_result_ok; mod unused_rounding; mod unused_self; +mod unused_trait_names; mod unused_unit; mod unwrap; mod unwrap_in_result; @@ -386,9 +391,10 @@ mod write; mod zero_div_zero; mod zero_repeat_side_effects; mod zero_sized_map_values; +mod zombie_processes; // end lints modules, do not remove this comment, it’s used in `update_lints` -use clippy_config::{get_configuration_metadata, Conf}; +use clippy_config::{Conf, get_configuration_metadata}; use clippy_utils::macros::FormatArgsStorage; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; @@ -623,7 +629,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(unit_return_expecting_ord::UnitReturnExpectingOrd)); store.register_late_pass(|_| Box::new(strings::StringAdd)); store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); - store.register_late_pass(|_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub)); + store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); @@ -632,8 +638,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { let format_args = format_args_storage.clone(); store.register_late_pass(move |_| Box::new(methods::Methods::new(conf, format_args.clone()))); store.register_late_pass(move |_| Box::new(matches::Matches::new(conf))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(conf))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(conf))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustive::new(conf))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(conf))); store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(conf))); store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(conf))); @@ -933,5 +938,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert)); store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice)); store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest)); + store.register_late_pass(|_| Box::new(zombie_processes::ZombieProcesses)); + store.register_late_pass(|_| Box::new(pointers_in_nomem_asm_block::PointersInNomemAsmBlock)); + store.register_late_pass(move |_| Box::new(manual_div_ceil::ManualDivCeil::new(conf))); + store.register_late_pass(|_| Box::new(manual_is_power_of_two::ManualIsPowerOfTwo)); + store.register_late_pass(|_| Box::new(non_zero_suggestions::NonZeroSuggestions)); + store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index ba34af9c100e..755afe959b37 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -3,25 +3,25 @@ use clippy_utils::trait_ref_of_method; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; +use rustc_hir::FnRetTy::Return; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, - walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, + Visitor, walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_poly_trait_ref, walk_trait_ref, walk_ty, }; -use rustc_hir::FnRetTy::Return; use rustc_hir::{ - lang_items, BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, - Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, - PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, + BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, Impl, + ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef, + PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter as middle_nested_filter; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; +use rustc_span::symbol::{Ident, Symbol, kw}; use std::ops::ControlFlow; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs index 73a23615c2dc..9aa4d2f0adc2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs @@ -1,4 +1,4 @@ -use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP}; +use super::{EXPLICIT_COUNTER_LOOP, IncrementVisitor, InitializeVisitor, make_iterator_snippet}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::{get_enclosing_block, is_integer_const}; diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs index 5b5bb88c1790..858e3be5093e 100644 --- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed}; -use hir::intravisit::{walk_expr, Visitor}; +use hir::intravisit::{Visitor, walk_expr}; use hir::{Expr, ExprKind, FnRetTy, FnSig, Node}; use rustc_ast::Label; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs index b27528c11d48..bfe2e68b5d1f 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::MANUAL_FIND; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs index dbc094a6d73f..366c310592f5 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::MANUAL_FLATTEN; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::visitors::is_local_used; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs index 55d1b9ee6767..7476a87267f3 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_while_let_some.rs @@ -1,10 +1,10 @@ +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; -use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Pat, Stmt, StmtKind, UnOp}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol, Span}; +use rustc_span::{Span, Symbol, sym}; use std::borrow::Cow; use super::MANUAL_WHILE_LET_SOME; diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs index 92ccc0cc0a15..17215621d2aa 100644 --- a/src/tools/clippy/clippy_lints/src/loops/mod.rs +++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs @@ -22,15 +22,15 @@ mod while_immutable_condition; mod while_let_loop; mod while_let_on_iterator; -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::higher; use rustc_ast::Label; use rustc_hir::{Expr, ExprKind, LoopSource, Pat}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; -use utils::{make_iterator_snippet, IncrementVisitor, InitializeVisitor}; +use utils::{IncrementVisitor, InitializeVisitor, make_iterator_snippet}; declare_clippy_lint! { /// ### What it does @@ -188,22 +188,22 @@ declare_clippy_lint! { /// The `while let` loop is usually shorter and more /// readable. /// - /// ### Known problems - /// Sometimes the wrong binding is displayed ([#383](https://github.com/rust-lang/rust-clippy/issues/383)). - /// /// ### Example /// ```rust,no_run - /// # let y = Some(1); + /// let y = Some(1); /// loop { /// let x = match y { /// Some(x) => x, /// None => break, /// }; - /// // .. do something with x + /// // .. /// } - /// // is easier written as + /// ``` + /// Use instead: + /// ```rust,no_run + /// let y = Some(1); /// while let Some(x) = y { - /// // .. do something with x + /// // .. /// }; /// ``` #[clippy::version = "pre 1.29.0"] diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs index e18e4374667d..20dd5a311dca 100644 --- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs @@ -3,17 +3,17 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::has_iter_method; use clippy_utils::visitors::is_local_used; -use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; +use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath}; use rustc_lint::LateContext; use rustc_middle::middle::region; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use std::{iter, mem}; /// Checks for looping over a range and then indexing a sequence with it. 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 313a5bfefbc8..1c55e3e22e8c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs @@ -1,5 +1,5 @@ -use super::utils::make_iterator_snippet; use super::NEVER_LOOP; +use super::utils::make_iterator_snippet; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; @@ -7,7 +7,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::iter::once; pub(super) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs index 9185cf1f8b2e..5662d3013e15 100644 --- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs +++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs @@ -6,11 +6,11 @@ use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, Mutability, Node, Pat, PatKind, Stmt, StmtKind}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::SyntaxContext; +use rustc_span::symbol::sym; /// Detects for loop pushing the same item into a Vec pub(super) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs index 2b41911e8ec7..70f76ced09a4 100644 --- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs +++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs @@ -2,10 +2,10 @@ use super::SINGLE_ELEMENT_LOOP; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{indent_of, snippet, snippet_with_applicability}; use clippy_utils::visitors::contains_break_or_continue; -use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::Mutability; +use rustc_ast::util::parser::PREC_PREFIX; use rustc_errors::Applicability; -use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat, PatKind}; +use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal}; use rustc_lint::LateContext; use rustc_span::edition::Edition; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index 7b45cc95431c..9a89a41d2b39 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -2,13 +2,13 @@ use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_local, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; use rustc_span::source_map::Spanned; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; #[derive(Debug, PartialEq, Eq)] enum IncrementVisitorVarState { diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs index cc1bd5929d0f..eab096e9a227 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::usage::mutated_variables; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Expr, ExprKind, HirIdSet, QPath}; use rustc_lint::LateContext; use std::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index c171fa1c622a..7d9fbaf3ceab 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -5,13 +5,13 @@ use clippy_utils::visitors::is_res_used; use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LangItem, LetStmt, Mutability, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::symbol::sym; use rustc_span::Symbol; +use rustc_span::symbol::sym; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr) diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index e215097142be..ccc554042d68 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_lint_allowed; use itertools::Itertools; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; -use std::collections::btree_map::Entry; +use rustc_span::{Span, SyntaxContext, sym}; use std::collections::BTreeMap; +use std::collections::btree_map::Entry; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs index 067384b09015..bd6b3f1a47b1 100644 --- a/src/tools/clippy/clippy_lints/src/macro_use.rs +++ b/src/tools/clippy/clippy_lints/src/macro_use.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::collections::BTreeMap; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index 61723aec5904..fc3bba9e5123 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -9,7 +9,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs index 5cbdc7f864a7..c0e87e8a1fae 100644 --- a/src/tools/clippy/clippy_lints/src/manual_bits.rs +++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::get_parent_expr; use clippy_utils::source::snippet_with_context; diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 4123c933660b..788649fd4f90 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::higher::If; @@ -7,8 +7,8 @@ use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::is_const_evaluatable; use clippy_utils::{ - eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, MaybePath, + MaybePath, eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, + peel_blocks, peel_blocks_with_stmt, }; use itertools::Itertools; use rustc_errors::{Applicability, Diag}; @@ -17,8 +17,8 @@ use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegme use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use std::cmp::Ordering; use std::ops::Deref; diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs new file mode 100644 index 000000000000..00e02e2a3365 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs @@ -0,0 +1,158 @@ +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use clippy_utils::sugg::Sugg; +use rustc_ast::{BinOpKind, LitKind}; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Symbol; + +use clippy_config::Conf; + +declare_clippy_lint! { + /// ### What it does + /// Checks for an expression like `(x + (y - 1)) / y` which is a common manual reimplementation + /// of `x.div_ceil(y)`. + /// + /// ### Why is this bad? + /// It's simpler, clearer and more readable. + /// + /// ### Example + /// ```no_run + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = (x + (y - 1)) / y; + /// ``` + /// Use instead: + /// ```no_run + /// #![feature(int_roundings)] + /// let x: i32 = 7; + /// let y: i32 = 4; + /// let div = x.div_ceil(y); + /// ``` + #[clippy::version = "1.81.0"] + pub MANUAL_DIV_CEIL, + complexity, + "manually reimplementing `div_ceil`" +} + +pub struct ManualDivCeil { + msrv: Msrv, +} + +impl ManualDivCeil { + #[must_use] + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]); + +impl<'tcx> LateLintPass<'tcx> for ManualDivCeil { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { + if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) { + return; + } + + let mut applicability = Applicability::MachineApplicable; + + if let ExprKind::Binary(div_op, div_lhs, div_rhs) = expr.kind + && div_op.node == BinOpKind::Div + && check_int_ty_and_feature(cx, div_lhs) + && check_int_ty_and_feature(cx, div_rhs) + && let ExprKind::Binary(inner_op, inner_lhs, inner_rhs) = div_lhs.kind + { + // (x + (y - 1)) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_rhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_lhs, div_rhs, &mut applicability); + return; + } + + // ((y - 1) + x) / y + if let ExprKind::Binary(sub_op, sub_lhs, sub_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Add + && sub_op.node == BinOpKind::Sub + && check_literal(sub_rhs) + && check_eq_expr(cx, sub_lhs, div_rhs) + { + build_suggestion(cx, expr, inner_rhs, div_rhs, &mut applicability); + return; + } + + // (x + y - 1) / y + if let ExprKind::Binary(add_op, add_lhs, add_rhs) = inner_lhs.kind + && inner_op.node == BinOpKind::Sub + && add_op.node == BinOpKind::Add + && check_literal(inner_rhs) + && check_eq_expr(cx, add_rhs, div_rhs) + { + build_suggestion(cx, expr, add_lhs, div_rhs, &mut applicability); + } + } + } + + extract_msrv_attr!(LateContext); +} + +fn check_int_ty_and_feature(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + let expr_ty = cx.typeck_results().expr_ty(expr); + match expr_ty.peel_refs().kind() { + ty::Uint(_) => true, + ty::Int(_) => cx + .tcx + .features() + .declared_features + .contains(&Symbol::intern("int_roundings")), + + _ => false, + } +} + +fn check_literal(expr: &Expr<'_>) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(1), _) = lit.node + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} + +fn build_suggestion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + lhs: &Expr<'_>, + rhs: &Expr<'_>, + applicability: &mut Applicability, +) { + let dividend_sugg = Sugg::hir_with_applicability(cx, lhs, "..", applicability).maybe_par(); + let divisor_snippet = snippet_with_applicability(cx, rhs.span.source_callsite(), "..", applicability); + + let sugg = format!("{dividend_sugg}.div_ceil({divisor_snippet})"); + + span_lint_and_sugg( + cx, + MANUAL_DIV_CEIL, + expr.span, + "manually reimplementing `div_ceil`", + "consider using `.div_ceil()`", + sugg, + *applicability, + ); +} diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 11716a539ab5..c62bd65bea68 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::SpanRangeExt; use clippy_utils::visitors::{is_local_used, local_used_once}; diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs index 31c37c3bc3b1..24207705743d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs +++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs @@ -1,17 +1,17 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators}; -use rustc_ast::ast::RangeLimits; use rustc_ast::LitKind::{Byte, Char}; +use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Param, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs new file mode 100644 index 000000000000..da2a982ee170 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs @@ -0,0 +1,142 @@ +use clippy_utils::SpanlessEq; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_with_applicability; +use rustc_ast::LitKind; +use rustc_data_structures::packed::Pu128; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Uint; +use rustc_session::declare_lint_pass; + +declare_clippy_lint! { + /// ### What it does + /// Checks for expressions like `x.count_ones() == 1` or `x & (x - 1) == 0`, with x and unsigned integer, which are manual + /// reimplementations of `x.is_power_of_two()`. + /// ### Why is this bad? + /// Manual reimplementations of `is_power_of_two` increase code complexity for little benefit. + /// ### Example + /// ```no_run + /// let a: u32 = 4; + /// let result = a.count_ones() == 1; + /// ``` + /// Use instead: + /// ```no_run + /// let a: u32 = 4; + /// let result = a.is_power_of_two(); + /// ``` + #[clippy::version = "1.82.0"] + pub MANUAL_IS_POWER_OF_TWO, + complexity, + "manually reimplementing `is_power_of_two`" +} + +declare_lint_pass!(ManualIsPowerOfTwo => [MANUAL_IS_POWER_OF_TWO]); + +impl LateLintPass<'_> for ManualIsPowerOfTwo { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + let mut applicability = Applicability::MachineApplicable; + + if let ExprKind::Binary(bin_op, left, right) = expr.kind + && bin_op.node == BinOpKind::Eq + { + // a.count_ones() == 1 + if let ExprKind::MethodCall(method_name, reciever, _, _) = left.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(right, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } + + // 1 == a.count_ones() + if let ExprKind::MethodCall(method_name, reciever, _, _) = right.kind + && method_name.ident.as_str() == "count_ones" + && let &Uint(_) = cx.typeck_results().expr_ty(reciever).kind() + && check_lit(left, 1) + { + build_sugg(cx, expr, reciever, &mut applicability); + } + + // a & (a - 1) == 0 + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // (a - 1) & a == 0; + if let ExprKind::Binary(op1, left1, right1) = left.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(right, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } + + // 0 == a & (a - 1); + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = right1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, left1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(left1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, left1, &mut applicability); + } + + // 0 == (a - 1) & a + if let ExprKind::Binary(op1, left1, right1) = right.kind + && op1.node == BinOpKind::BitAnd + && let ExprKind::Binary(op2, left2, right2) = left1.kind + && op2.node == BinOpKind::Sub + && check_eq_expr(cx, right1, left2) + && let &Uint(_) = cx.typeck_results().expr_ty(right1).kind() + && check_lit(right2, 1) + && check_lit(left, 0) + { + build_sugg(cx, expr, right1, &mut applicability); + } + } + } +} + +fn build_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, reciever: &Expr<'_>, applicability: &mut Applicability) { + let snippet = snippet_with_applicability(cx, reciever.span, "..", applicability); + + span_lint_and_sugg( + cx, + MANUAL_IS_POWER_OF_TWO, + expr.span, + "manually reimplementing `is_power_of_two`", + "consider using `.is_power_of_two()`", + format!("{snippet}.is_power_of_two()"), + *applicability, + ); +} + +fn check_lit(expr: &Expr<'_>, expected_num: u128) -> bool { + if let ExprKind::Lit(lit) = expr.kind + && let LitKind::Int(Pu128(num), _) = lit.node + && num == expected_num + { + return true; + } + false +} + +fn check_eq_expr(cx: &LateContext<'_>, lhs: &Expr<'_>, rhs: &Expr<'_>) -> bool { + SpanlessEq::new(cx).eq_expr(lhs, rhs) +} diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs index ebebdf679a86..17185df5d76f 100644 --- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs +++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs @@ -1,4 +1,4 @@ -use crate::question_mark::{QuestionMark, QUESTION_MARK}; +use crate::question_mark::{QUESTION_MARK, QuestionMark}; use clippy_config::msrvs; use clippy_config::types::MatchLintBehaviour; use clippy_utils::diagnostics::span_lint_and_then; @@ -12,8 +12,8 @@ use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; use std::slice; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs index 5198d7838a28..85e99a92cf59 100644 --- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs +++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_trait_method, peel_hir_expr_refs}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs index 6b1d90483ccd..25868ccae400 100644 --- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs +++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs @@ -1,18 +1,18 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_doc_hidden; -use clippy_utils::source::SpanRangeExt; -use rustc_ast::ast::{self, VisibilityKind}; +use clippy_utils::source::snippet_indent; +use itertools::Itertools; use rustc_ast::attr; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; -use rustc_hir::{self as hir, Expr, ExprKind, QPath}; -use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; +use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{sym, Span}; +use rustc_span::def_id::LocalDefId; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -62,29 +62,13 @@ declare_clippy_lint! { "manual implementations of the non-exhaustive pattern can be simplified using #[non_exhaustive]" } -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveStruct { +pub struct ManualNonExhaustive { msrv: Msrv, -} - -impl ManualNonExhaustiveStruct { - pub fn new(conf: &'static Conf) -> Self { - Self { - msrv: conf.msrv.clone(), - } - } -} - -impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); - -#[expect(clippy::module_name_repetitions)] -pub struct ManualNonExhaustiveEnum { - msrv: Msrv, - constructed_enum_variants: FxHashSet<(DefId, DefId)>, + constructed_enum_variants: FxHashSet, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } -impl ManualNonExhaustiveEnum { +impl ManualNonExhaustive { pub fn new(conf: &'static Conf) -> Self { Self { msrv: conf.msrv.clone(), @@ -94,96 +78,78 @@ impl ManualNonExhaustiveEnum { } } -impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); +impl_lint_pass!(ManualNonExhaustive => [MANUAL_NON_EXHAUSTIVE]); -impl EarlyLintPass for ManualNonExhaustiveStruct { - fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if let ast::ItemKind::Struct(variant_data, _) = &item.kind - && let (fields, delimiter) = match variant_data { - ast::VariantData::Struct { fields, .. } => (&**fields, '{'), - ast::VariantData::Tuple(fields, _) => (&**fields, '('), - ast::VariantData::Unit(_) => return, - } - && fields.len() > 1 - && self.msrv.meets(msrvs::NON_EXHAUSTIVE) - { - let mut iter = fields.iter().filter_map(|f| match f.vis.kind { - VisibilityKind::Public => None, - VisibilityKind::Inherited => Some(Ok(f)), - VisibilityKind::Restricted { .. } => Some(Err(())), - }); - if let Some(Ok(field)) = iter.next() - && iter.next().is_none() - && field.ty.kind.is_unit() - { - span_lint_and_then( - cx, - MANUAL_NON_EXHAUSTIVE, - item.span, - "this seems like a manual implementation of the non-exhaustive pattern", - |diag| { - if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive)) - && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter) - && let Some(snippet) = header_span.get_source_text(cx) - { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } - diag.span_help(field.span, "remove this field"); - }, - ); - } - } - } - - extract_msrv_attr!(EarlyContext); -} - -impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { +impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustive { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) || !cx.effective_visibilities.is_exported(item.owner_id.def_id) { return; } - if let hir::ItemKind::Enum(def, _) = &item.kind - && def.variants.len() > 1 - { - let mut iter = def.variants.iter().filter_map(|v| { - (matches!(v.data, hir::VariantData::Unit(_, _)) - && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id)) - && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive)) - .then_some((v.def_id, v.span)) - }); - if let Some((id, span)) = iter.next() - && iter.next().is_none() - { - self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); - } + match item.kind { + ItemKind::Enum(def, _) if def.variants.len() > 1 => { + let iter = def.variants.iter().filter_map(|v| { + (matches!(v.data, VariantData::Unit(_, _)) && is_doc_hidden(cx.tcx.hir().attrs(v.hir_id))) + .then_some((v.def_id, v.span)) + }); + if let Ok((id, span)) = iter.exactly_one() + && !attr::contains_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + self.potential_enums.push((item.owner_id.def_id, id, item.span, span)); + } + }, + ItemKind::Struct(variant_data, _) => { + let fields = variant_data.fields(); + let private_fields = fields + .iter() + .filter(|field| !cx.effective_visibilities.is_exported(field.def_id)); + if fields.len() > 1 + && let Ok(field) = private_fields.exactly_one() + && let TyKind::Tup([]) = field.ty.kind + { + span_lint_and_then( + cx, + MANUAL_NON_EXHAUSTIVE, + item.span, + "this seems like a manual implementation of the non-exhaustive pattern", + |diag| { + if let Some(non_exhaustive) = + attr::find_by_name(cx.tcx.hir().attrs(item.hir_id()), sym::non_exhaustive) + { + diag.span_note(non_exhaustive.span, "the struct is already non-exhaustive"); + } else { + let indent = snippet_indent(cx, item.span).unwrap_or_default(); + diag.span_suggestion_verbose( + item.span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); + } + diag.span_help(field.span, "remove this field"); + }, + ); + } + }, + _ => {}, } } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Path(QPath::Resolved(None, p)) = &e.kind - && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res + && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), ctor_id) = p.res + && let Some(local_ctor) = ctor_id.as_local() { - let variant_id = cx.tcx.parent(id); - let enum_id = cx.tcx.parent(variant_id); - - self.constructed_enum_variants.insert((enum_id, variant_id)); + let variant_id = cx.tcx.local_parent(local_ctor); + self.constructed_enum_variants.insert(variant_id); } } fn check_crate_post(&mut self, cx: &LateContext<'tcx>) { - for &(enum_id, _, enum_span, variant_span) in - self.potential_enums.iter().filter(|&&(enum_id, variant_id, _, _)| { - !self - .constructed_enum_variants - .contains(&(enum_id.to_def_id(), variant_id.to_def_id())) - }) + for &(enum_id, _, enum_span, variant_span) in self + .potential_enums + .iter() + .filter(|(_, variant_id, _, _)| !self.constructed_enum_variants.contains(variant_id)) { let hir_id = cx.tcx.local_def_id_to_hir_id(enum_id); span_lint_hir_and_then( @@ -193,15 +159,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { enum_span, "this seems like a manual implementation of the non-exhaustive pattern", |diag| { - let header_span = cx.sess().source_map().span_until_char(enum_span, '{'); - if let Some(snippet) = header_span.get_source_text(cx) { - diag.span_suggestion( - header_span, - "add the attribute", - format!("#[non_exhaustive] {snippet}"), - Applicability::Unspecified, - ); - } + let indent = snippet_indent(cx, enum_span).unwrap_or_default(); + diag.span_suggestion_verbose( + enum_span.shrink_to_lo(), + "use the `#[non_exhaustive]` attribute instead", + format!("#[non_exhaustive]\n{indent}"), + Applicability::MaybeIncorrect, + ); diag.span_help(variant_span, "remove this variant"); }, ); diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index 9afb2b5bc84c..ffa3eacf3544 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -7,7 +7,7 @@ use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd, UnOp}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span}; declare_clippy_lint! { /// ### What it does @@ -77,9 +77,10 @@ impl Num { impl LateLintPass<'_> for ManualRangePatterns { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) { // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives - // or at least one range + // or more then one range (exclude triggering on stylistic using OR with one element + // like described https://github.com/rust-lang/rust-clippy/issues/11825) if let PatKind::Or(pats) = pat.kind - && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..)))) + && (pats.len() >= 3 || (pats.len() > 1 && pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))) && !in_external_macro(cx.sess(), pat.span) { let mut min = Num::dummy(i128::MAX); diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs index 6cf5d272d7d8..86293169ea28 100644 --- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs +++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_context; diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs index a35b0f914e05..a60163be770d 100644 --- a/src/tools/clippy/clippy_lints/src/manual_retain.rs +++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs @@ -1,17 +1,17 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item}; -use clippy_utils::SpanlessEq; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::def_id::DefId; use rustc_hir::ExprKind::Assign; +use rustc_hir::def_id::DefId; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; const ACCEPTABLE_METHODS: [Symbol; 5] = [ sym::binaryheap_iter, diff --git a/src/tools/clippy/clippy_lints/src/manual_string_new.rs b/src/tools/clippy/clippy_lints/src/manual_string_new.rs index 781db4b97f08..198f7aaddc71 100644 --- a/src/tools/clippy/clippy_lints/src/manual_string_new.rs +++ b/src/tools/clippy/clippy_lints/src/manual_string_new.rs @@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind, PathSegment, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, symbol, Span}; +use rustc_span::{Span, sym, symbol}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs index 02d433ecc5aa..9aceca66bf77 100644 --- a/src/tools/clippy/clippy_lints/src/manual_strip.rs +++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; @@ -8,13 +8,13 @@ use clippy_utils::{eq_expr_value, higher}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::iter; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs index 2db71b1f7a38..a97dbbbc33fd 100644 --- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs +++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index b666e77c09ec..50e6dfc6298d 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -4,7 +4,7 @@ use clippy_utils::higher::IfLetOrMatch; use clippy_utils::source::snippet; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, + SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, }; use rustc_errors::MultiSpan; use rustc_hir::LangItem::OptionNone; @@ -12,7 +12,7 @@ use rustc_hir::{Arm, Expr, HirId, Pat, PatKind}; use rustc_lint::LateContext; use rustc_span::Span; -use super::{pat_contains_disallowed_or, COLLAPSIBLE_MATCH}; +use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or}; pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: &Msrv) { if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) { diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs index 619ec83127ab..cfa054706d6b 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_filter.rs @@ -6,10 +6,10 @@ use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; -use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_FILTER; +use super::manual_utils::{SomeExpr, check_with}; // Function called on the of `[&+]Some((ref | ref mut) x) => ` // Need to check if it's of the form `=if {} else {}` diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs index ed3d8b09fdcd..de57d1eee924 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_map.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_map.rs @@ -1,5 +1,5 @@ -use super::manual_utils::{check_with, SomeExpr}; use super::MANUAL_MAP; +use super::manual_utils::{SomeExpr, check_with}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::{is_res_lang_ctor, path_res}; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index d5d83df93471..59d375200117 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::contains_return_break_continue_macro; use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg}; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{OptionNone, ResultErr}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, Pat, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs index be80aebed6df..d38560998a5a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs @@ -4,16 +4,16 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function}; use clippy_utils::{ - can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, path_to_local_id, - peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, CaptureKind, + CaptureKind, can_move_expr_to_closure, is_else_clause, is_lint_allowed, is_res_lang_ctor, path_res, + path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while, }; use rustc_ast::util::parser::PREC_UNAMBIGUOUS; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome}; +use rustc_hir::def::Res; use rustc_hir::{BindingMode, Expr, ExprKind, HirId, Mutability, Pat, PatKind, Path, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; #[expect(clippy::too_many_arguments)] #[expect(clippy::too_many_lines)] diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index da8c918a62bb..f9ffbc5dc0b8 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash}; +use clippy_utils::{SpanlessEq, SpanlessHash, is_lint_allowed, path_to_local, search_same}; use core::cmp::Ordering; use core::{iter, slice}; use rustc_arena::DroplessArena; diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs index 322e9c3ebe5b..40518ce2ca77 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs @@ -2,12 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Arm, Expr, ExprKind, LangItem, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use super::MATCH_STR_CASE_MISMATCH; diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index cf5377e0725d..686fc4a0fa0a 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -24,8 +24,8 @@ mod single_match; mod try_err; mod wild_in_or_pats; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::source::walk_span_to_context; use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg}; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; diff --git a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs index 71f211be925b..6a4c553cee0e 100644 --- a/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/overlapping_arms.rs @@ -1,4 +1,4 @@ -use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt}; +use clippy_utils::consts::{ConstEvalCtxt, FullInt, mir_to_const}; use clippy_utils::diagnostics::span_lint_and_note; use core::cmp::Ordering; use rustc_hir::{Arm, Expr, PatKind, RangeEnd}; 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 8222c3057f73..564c598a334c 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs @@ -10,11 +10,11 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp}; use rustc_lint::LateContext; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use std::borrow::Cow; use std::ops::ControlFlow; -use super::{pat_contains_disallowed_or, REDUNDANT_GUARDS}; +use super::{REDUNDANT_GUARDS, pat_contains_disallowed_or}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv: &Msrv) { for outer_arm in arms { diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs index 0e4b2d9d34a0..b646e87a4393 100644 --- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs @@ -1,18 +1,18 @@ use super::REDUNDANT_PATTERN_MATCHING; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, walk_span_to_context}; -use clippy_utils::sugg::{make_unop, Sugg}; +use clippy_utils::sugg::{Sugg, make_unop}; use clippy_utils::ty::{is_type_diagnostic_item, needs_ordered_drop}; use clippy_utils::visitors::{any_temporaries_need_ordered_drop, for_each_expr_without_closures}; use clippy_utils::{higher, is_expn_of, is_trait_method}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; -use rustc_hir::def::{DefKind, Res}; use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk}; +use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::{self, GenericArgKind, Ty}; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use std::fmt::Write; use std::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs index 9c2f42d21870..537f7272f7f3 100644 --- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs +++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs @@ -8,7 +8,7 @@ use clippy_utils::{get_attr, is_lint_allowed}; use itertools::Itertools; use rustc_ast::Mutability; use rustc_errors::{Applicability, Diag}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Arm, Expr, ExprKind, MatchSource}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor}; diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index b6930f7b9d10..2eda238ae8cd 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{expr_block, snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, expr_block, snippet, snippet_block_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{ is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs, @@ -8,11 +8,11 @@ use core::ops::ControlFlow; use rustc_arena::DroplessArena; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_pat, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath}; +use rustc_hir::intravisit::{Visitor, walk_pat}; +use rustc_hir::{Arm, Expr, ExprKind, HirId, Node, Pat, PatKind, QPath, StmtKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; @@ -91,6 +91,29 @@ fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, exp format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app)) }); + if snippet(cx, ex.span, "..") == snippet(cx, arm.pat.span, "..") { + let msg = "this pattern is irrefutable, `match` is useless"; + let (sugg, help) = if is_unit_expr(arm.body) { + (String::new(), "`match` expression can be removed") + } else { + let mut sugg = snippet_block_with_context(cx, arm.body.span, ctxt, "..", Some(expr.span), &mut app) + .0 + .to_string(); + if let Node::Stmt(stmt) = cx.tcx.parent_hir_node(expr.hir_id) + && let StmtKind::Expr(_) = stmt.kind + && match arm.body.kind { + ExprKind::Block(block, _) => block.span.from_expansion(), + _ => true, + } + { + sugg.push(';'); + } + (sugg, "try") + }; + span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg.to_string(), app); + return; + } + let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat); let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex)) diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs index 72a5945ad9bc..146748734cff 100644 --- a/src/tools/clippy/clippy_lints/src/mem_replace.rs +++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; @@ -13,8 +13,8 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs index 2a8a5fcc3b6f..91a5de16e967 100644 --- a/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/bind_instead_of_map.rs @@ -1,4 +1,4 @@ -use super::{contains_return, BIND_INSTEAD_OF_MAP}; +use super::{BIND_INSTEAD_OF_MAP, contains_return}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::peel_blocks; use clippy_utils::source::{snippet, snippet_with_context}; diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 740cce0a6c23..76bdbe55e2f3 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_lang_item; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem}; use rustc_lint::LateContext; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; use super::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS; diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index 5389861245a2..6e24cabca8bb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -4,8 +4,8 @@ use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, LangItem, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::CLEAR_WITH_DRAIN; diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs index e7a2060be046..79a473e0e6f5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs @@ -7,7 +7,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_COPY; diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs index e0826b53004b..96e2de0dc1cb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::CLONE_ON_REF_PTR; diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs index fcafa1622365..fa04f74eec10 100644 --- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::CLONED_INSTEAD_OF_COPIED; diff --git a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs index 1fab6c0e499d..f7bf8764bdeb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/collapsible_str_replace.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_lint::LateContext; use std::collections::VecDeque; -use super::{method_call, COLLAPSIBLE_STR_REPLACE}; +use super::{COLLAPSIBLE_STR_REPLACE, method_call}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 56171a134522..10360b4817bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; /// Checks if both types match the given diagnostic item, e.g.: /// diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs index dc978c8a5845..3b1adb16b80e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; pub(super) fn check( cx: &LateContext<'_>, diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index c9f56e1d9809..2922086522c2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage}; +use clippy_utils::macros::{FormatArgsStorage, format_args_inputs_span, root_macro_call_first_node}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use std::borrow::Cow; use super::EXPECT_FUN_CALL; diff --git a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs index 2ab0401947c4..35008c39c084 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filetype_is_file.rs @@ -3,7 +3,7 @@ use clippy_utils::get_parent_expr; use clippy_utils::ty::is_type_diagnostic_item; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FILETYPE_IS_FILE; diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs index 02a11257007a..06482a743ddf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::macros::{is_panic, matching_root_macro_call, root_macro_call}; use clippy_utils::source::{indent_of, reindent_multiline, snippet}; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{higher, is_trait_method, path_to_local_id, peel_blocks, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, is_trait_method, path_to_local_id, peel_blocks}; use hir::{Body, HirId, MatchSource, Pat}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -10,8 +10,8 @@ use rustc_hir::def::Res; use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, sym}; use std::borrow::Cow; use super::{MANUAL_FILTER_MAP, MANUAL_FIND_MAP, OPTION_FILTER_MAP, RESULT_FILTER_MAP}; 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 77a62fbb4fb9..129e69254289 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 @@ -7,9 +7,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::adjustment::Adjust; use rustc_middle::ty::Binder; -use rustc_span::{sym, Span}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{Span, sym}; 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) diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs index 999df875c753..cf7f276dabb1 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_identity.rs @@ -3,7 +3,7 @@ use clippy_utils::{is_expr_identity_function, is_expr_untyped_identity_function, use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FILTER_MAP_IDENTITY; diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs index 651ea34f9d0e..0c2ecfbc8ffd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_identity.rs @@ -3,7 +3,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FLAT_MAP_IDENTITY; diff --git a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs index 0a4a381b8618..3242dcadb701 100644 --- a/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs +++ b/src/tools/clippy/clippy_lints/src/methods/flat_map_option.rs @@ -4,7 +4,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::FLAT_MAP_OPTION; use clippy_utils::ty::is_type_diagnostic_item; diff --git a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs index 620376511342..5f6fb4c821d5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/get_last_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_integer_literal, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_integer_literal}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs index 230a8eb2ec47..4ed7de81ea3d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; +use rustc_span::symbol::{Symbol, sym}; use super::INEFFICIENT_TO_STRING; diff --git a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs index ad4b6fa130e3..3706f3b670ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/methods/inspect_for_each.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::is_trait_method; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::INSPECT_FOR_EACH; diff --git a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs index bbc7ce8d78ab..bedeb63367d0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/into_iter_on_ref.rs @@ -5,8 +5,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Symbol, sym}; use super::INTO_ITER_ON_REF; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs index b93d51eac647..f6612c984a7e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs @@ -10,8 +10,8 @@ use clippy_utils::{get_parent_expr, is_trait_method, peel_blocks, span_contains_ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::QPath; -use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{Ident, Symbol, sym}; use std::borrow::Cow; /// diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs index 33de3b87abc8..390dd24b5058 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs @@ -14,7 +14,6 @@ use rustc_span::sym; /// - `hashmap.into_iter().map(|(_, v)| v)` /// /// on `HashMaps` and `BTreeMaps` in std - pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, map_type: &'tcx str, // iter / into_iter diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs index e31fa2f777d8..82bda5d95122 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_nth.rs @@ -3,8 +3,8 @@ use clippy_utils::ty::get_type_diagnostic_name; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::ITER_NTH; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs index 7f6b666e434e..9d562f5e51d2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs @@ -5,9 +5,9 @@ use clippy_utils::source::snippet; use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core}; use rustc_errors::Applicability; +use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::def_id::DefId; use rustc_hir::hir_id::HirId; -use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 1378a07cbc47..163058713377 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -3,8 +3,8 @@ use clippy_utils::is_range_full; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::ITER_WITH_DRAIN; diff --git a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs index 2dad7fcf3c12..2ad070793cbb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/methods/join_absolute_paths.rs @@ -6,8 +6,8 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::JOIN_ABSOLUTE_PATHS; diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs index cb9fb373c10a..96af9db1af70 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs @@ -6,7 +6,7 @@ use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; use super::MANUAL_C_STR_LITERALS; diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index cac2e11f5916..64c19c327b25 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -3,13 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{IntoSpan, SpanRangeExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; -use clippy_utils::{expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id, ExprUseNode}; +use clippy_utils::{ExprUseNode, expr_use_ctxt, is_diag_item_method, is_diag_trait_item, path_to_local_id}; use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use super::MANUAL_INSPECT; diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs index d29acd4622a0..c377abd62377 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MANUAL_IS_VARIANT_AND; diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index b1a8e1e5e470..4321dd6b0e09 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index 11fba35c16ea..31449d417701 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -8,7 +8,7 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MANUAL_TRY_FOLD; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs index 08ce7e204dd2..ac378ff3702b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs @@ -11,7 +11,7 @@ use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MAP_CLONE; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs index 22a03825194e..07a7a12b1627 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::MAP_FLATTEN; diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs index 5dd7b1b02ade..1f204de01da0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs @@ -4,7 +4,7 @@ use clippy_utils::{is_expr_untyped_identity_function, is_trait_method}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MAP_IDENTITY; diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index f61bb3a6bf48..7696dd16b251 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -111,6 +111,7 @@ mod uninit_assumed_init; mod unit_hash; mod unnecessary_fallible_conversions; mod unnecessary_filter_map; +mod unnecessary_first_then_check; mod unnecessary_fold; mod unnecessary_get_then_check; mod unnecessary_iter_cloned; @@ -131,8 +132,8 @@ mod waker_clone_wake; mod wrong_self_convention; mod zst_offset; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::macros::FormatArgsStorage; @@ -146,7 +147,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -441,6 +442,17 @@ declare_clippy_lint! { /// } /// } /// ``` + /// + /// Use instead: + /// ```no_run + /// # struct X; + /// impl X { + /// fn as_str(&self) -> &'static str { + /// // .. + /// # "" + /// } + /// } + /// ``` #[clippy::version = "pre 1.29.0"] pub WRONG_SELF_CONVENTION, style, @@ -3964,7 +3976,7 @@ declare_clippy_lint! { /// ```no_run /// let _ = 0; /// ``` - #[clippy::version = "1.78.0"] + #[clippy::version = "1.81.0"] pub UNNECESSARY_MIN_OR_MAX, complexity, "using 'min()/max()' when there is no need for it" @@ -4025,7 +4037,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.78.0"] pub MANUAL_C_STR_LITERALS, - pedantic, + complexity, r#"creating a `CStr` through functions when `c""` literals can be used"# } @@ -4099,7 +4111,7 @@ declare_clippy_lint! { /// ```no_run /// "foo".is_ascii(); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_CHARACTER_ITERATION, suspicious, "is_ascii() called on a char iterator" @@ -4126,6 +4138,34 @@ declare_clippy_lint! { "use of `map` returning the original item" } +declare_clippy_lint! { + /// ### What it does + /// Checks the usage of `.first().is_some()` or `.first().is_none()` to check if a slice is + /// empty. + /// + /// ### Why is this bad? + /// Using `.is_empty()` is shorter and better communicates the intention. + /// + /// ### Example + /// ```no_run + /// let v = vec![1, 2, 3]; + /// if v.first().is_none() { + /// // The vector is empty... + /// } + /// ``` + /// Use instead: + /// ```no_run + /// let v = vec![1, 2, 3]; + /// if v.is_empty() { + /// // The vector is empty... + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub UNNECESSARY_FIRST_THEN_CHECK, + complexity, + "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`" +} + pub struct Methods { avoid_breaking_exported_api: bool, msrv: Msrv, @@ -4283,6 +4323,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, MANUAL_C_STR_LITERALS, UNNECESSARY_GET_THEN_CHECK, + UNNECESSARY_FIRST_THEN_CHECK, NEEDLESS_CHARACTER_ITERATION, MANUAL_INSPECT, UNNECESSARY_MIN_OR_MAX, @@ -5055,6 +5096,9 @@ fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, Some(("get", f_recv, [arg], _, _)) => { unnecessary_get_then_check::check(cx, call_span, recv, f_recv, arg, is_some); }, + Some(("first", f_recv, [], _, _)) => { + unnecessary_first_then_check::check(cx, call_span, recv, f_recv, is_some); + }, _ => {}, } } @@ -5130,12 +5174,9 @@ impl ShouldImplTraitCase { fn lifetime_param_cond(&self, impl_item: &hir::ImplItem<'_>) -> bool { self.lint_explicit_lifetime || !impl_item.generics.params.iter().any(|p| { - matches!( - p.kind, - hir::GenericParamKind::Lifetime { - kind: hir::LifetimeParamKind::Explicit - } - ) + matches!(p.kind, hir::GenericParamKind::Lifetime { + kind: hir::LifetimeParamKind::Explicit + }) }) } } diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs index 1a0fce2876d4..83e8370f939b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::MUT_MUTEX_LOCK; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 332da722a37d..348f740e7ddf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -4,8 +4,8 @@ use rustc_lint::LateContext; use rustc_middle::ty; use rustc_span::Span; -use super::utils::get_last_chain_binding_hir_id; use super::NEEDLESS_CHARACTER_ITERATION; +use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::SpanRangeExt; use clippy_utils::{match_def_path, path_to_local_id, peel_blocks}; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs index f61923e5bf56..421c7a5e070d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs @@ -4,12 +4,12 @@ use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection}; use clippy_utils::{ - can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id, - CaptureKind, + CaptureKind, can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, + path_to_local_id, }; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr}; use rustc_hir::{ BindingMode, Block, Expr, ExprKind, HirId, HirIdSet, LetStmt, Mutability, Node, PatKind, Stmt, StmtKind, }; @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocKind, ClauseKind, EarlyBinder, GenericArg, GenericArgKind, Ty}; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; const NEEDLESS_COLLECT_MSG: &str = "avoid using `collect()` when not needed"; diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 9f714fdd47bb..538aa9097a40 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -4,8 +4,8 @@ use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::Expr; +use rustc_hir::def::Res; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs index a301a5f7d65b..32f32f1b2167 100644 --- a/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs +++ b/src/tools/clippy/clippy_lints/src/methods/no_effect_replace.rs @@ -1,6 +1,6 @@ +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_lang_item; -use clippy_utils::SpanlessEq; use rustc_ast::LitKind; use rustc_hir::{ExprKind, LangItem}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/methods/open_options.rs b/src/tools/clippy/clippy_lints/src/methods/open_options.rs index f1a3c81cebbd..da084871402a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/open_options.rs +++ b/src/tools/clippy/clippy_lints/src/methods/open_options.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS}; @@ -126,17 +126,13 @@ fn get_open_options( && let ExprKind::Path(path) = callee.kind && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id() { - let std_file_options = [ - sym::file_options, - sym::open_options_new, - ]; + let std_file_options = [sym::file_options, sym::open_options_new]; - let tokio_file_options: &[&[&str]] = &[ - &paths::TOKIO_IO_OPEN_OPTIONS_NEW, - &paths::TOKIO_FILE_OPTIONS, - ]; + let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS]; - let is_std_options = std_file_options.into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, did)); + let is_std_options = std_file_options + .into_iter() + .any(|sym| cx.tcx.is_diagnostic_item(sym, did)); is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some() } else { false diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs index ba167f9d9c23..9b22494888fb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_cloned.rs @@ -3,9 +3,9 @@ use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; -use super::{method_call, OPTION_AS_REF_CLONED}; +use super::{OPTION_AS_REF_CLONED, method_call}; pub(super) fn check(cx: &LateContext<'_>, cloned_recv: &Expr<'_>, cloned_ident_span: Span) { if let Some((method @ ("as_ref" | "as_mut"), as_ref_recv, [], as_ref_ident_span, _)) = method_call(cloned_recv) diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 9a18d8a14219..389e02056b2b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::OPTION_AS_REF_DEREF; @@ -48,7 +48,9 @@ pub(super) fn check( .map_or(false, |fun_def_id| { cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id) - || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) + || deref_aliases + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id)) }) }, hir::ExprKind::Closure(&hir::Closure { body, .. }) => { @@ -69,7 +71,9 @@ pub(super) fn check( let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap(); cx.tcx.is_diagnostic_item(sym::deref_method, method_did) || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did) - || deref_aliases.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) + || deref_aliases + .iter() + .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did)) } else { false } diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs index ed3bed42eb3a..b160ab6de8e9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs @@ -5,11 +5,11 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_path, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ExprKind, HirId, Node, PatKind, Path, QPath}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::ops::ControlFlow; use super::MAP_UNWRAP_OR; diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index af71799f0a73..c60a839432c9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -11,8 +11,8 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::symbol::{self, sym, Symbol}; use rustc_span::Span; +use rustc_span::symbol::{self, Symbol, sym}; use {rustc_ast as ast, rustc_hir as hir}; use super::{OR_FUN_CALL, UNWRAP_OR_DEFAULT}; diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs index 7b0bdcf99e73..3e64e15dc860 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::lang_items::LangItem; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::OR_THEN_UNWRAP; diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index 28ca76832eb3..f4d206c5307c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::source::snippet; -use clippy_utils::{higher, is_integer_const, is_trait_method, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, is_integer_const, is_trait_method}; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_span::sym; diff --git a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs index 3b2dd285b8c9..4ab165a5528b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/methods/search_is_some.rs @@ -8,8 +8,8 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use super::SEARCH_IS_SOME; diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs index 8bc535ac47a8..7ef07fe899c0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_from_current.rs @@ -6,9 +6,9 @@ use rustc_lint::LateContext; use rustc_span::sym; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_enum_variant_ctor; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; -use clippy_utils::is_enum_variant_ctor; use super::SEEK_FROM_CURRENT; diff --git a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs index 7c09cc252e13..9c966f010f13 100644 --- a/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs +++ b/src/tools/clippy/clippy_lints/src/methods/seek_to_start_instead_of_rewind.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::implements_trait; -use clippy_utils::{is_expr_used_or_unified, is_enum_variant_ctor}; +use clippy_utils::{is_enum_variant_ctor, is_expr_used_or_unified}; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::SEEK_TO_START_INSTEAD_OF_REWIND; diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index 12cabd43cb1b..69032776b2b9 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use rustc_errors::Applicability; @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span, Symbol, SyntaxContext}; +use rustc_span::{Span, Symbol, SyntaxContext, sym}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs index 38f2c9169124..c60a49067ec0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs +++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_command_arg_space.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::{Applicability, Diag}; use rustc_lint::LateContext; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use {rustc_ast as ast, rustc_hir as hir}; use super::SUSPICIOUS_COMMAND_ARG_SPACE; diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index db8cc4595d4d..e67ba5c4d314 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -7,7 +7,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; /// Checks if the given type is `dyn Any`, or a trait object that has `Any` as a supertrait. /// Only in those cases will its vtable have a `type_id` method that returns the implementor's diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs index d46b584f8f44..ce81282ddfeb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fallible_conversions.rs @@ -6,7 +6,7 @@ use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::print::with_forced_trimmed_paths; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_FALLIBLE_CONVERSIONS; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs index c9b9d98dbe60..ca46da81fa05 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_copy; use clippy_utils::usage::mutated_variables; -use clippy_utils::visitors::{for_each_expr_without_closures, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id}; use core::ops::ControlFlow; use rustc_hir as hir; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs new file mode 100644 index 000000000000..7ae1bb54e609 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -0,0 +1,56 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::SpanRangeExt; + +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::LateContext; +use rustc_span::Span; + +use super::UNNECESSARY_FIRST_THEN_CHECK; + +pub(super) fn check( + cx: &LateContext<'_>, + call_span: Span, + first_call: &Expr<'_>, + first_caller: &Expr<'_>, + is_some: bool, +) { + if !cx + .typeck_results() + .expr_ty_adjusted(first_caller) + .peel_refs() + .is_slice() + { + return; + } + + let ExprKind::MethodCall(_, _, _, first_call_span) = first_call.kind else { + return; + }; + + let both_calls_span = first_call_span.with_hi(call_span.hi()); + if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + { + let (sugg_span, suggestion) = if is_some { + ( + first_caller.span.with_hi(call_span.hi()), + format!("!{first_caller_snippet}.is_empty()"), + ) + } else { + (both_calls_span, "is_empty()".to_owned()) + }; + span_lint_and_sugg( + cx, + UNNECESSARY_FIRST_THEN_CHECK, + sugg_span, + format!( + "unnecessary use of `{both_calls_snippet}` to check if slice {}", + if is_some { "is not empty" } else { "is empty" } + ), + "replace this with", + suggestion, + Applicability::MaybeIncorrect, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs index ccc8d17970ec..b5d8972d7aad 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs @@ -8,7 +8,7 @@ use rustc_hir as hir; use rustc_hir::PatKind; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_FOLD; @@ -123,58 +123,32 @@ pub(super) fn check( if let hir::ExprKind::Lit(lit) = init.kind { match lit.node { ast::LitKind::Bool(false) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Or, - Replacement { - has_args: true, - has_generic_return: false, - method_name: "any", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + has_args: true, + has_generic_return: false, + method_name: "any", + }); }, ast::LitKind::Bool(true) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::And, - Replacement { - has_args: true, - has_generic_return: false, - method_name: "all", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + has_args: true, + has_generic_return: false, + method_name: "all", + }); }, - ast::LitKind::Int(Pu128(0), _) => check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Add, - Replacement { + ast::LitKind::Int(Pu128(0), _) => { + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { has_args: false, has_generic_return: needs_turbofish(cx, expr), method_name: "sum", - }, - ), + }); + }, ast::LitKind::Int(Pu128(1), _) => { - check_fold_with_op( - cx, - expr, - acc, - fold_span, - hir::BinOpKind::Mul, - Replacement { - has_args: false, - has_generic_return: needs_turbofish(cx, expr), - method_name: "product", - }, - ); + check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + has_args: false, + has_generic_return: needs_turbofish(cx, expr), + method_name: "product", + }); }, _ => (), } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index 64eb84117954..39fce2c40c91 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::Ty; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::UNNECESSARY_GET_THEN_CHECK; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 4d18bc7ac777..029704882dde 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -10,7 +10,7 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::UNNECESSARY_TO_OWNED; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs index 494d71fc053f..6dc8adb42dfb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_literal_unwrap.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{is_res_lang_ctor, last_path_segment, path_res, MaybePath}; +use clippy_utils::{MaybePath, is_res_lang_ctor, last_path_segment, path_res}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs index 86c0a6322b66..062d1348555c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs @@ -1,16 +1,15 @@ use std::cmp::Ordering; use super::UNNECESSARY_MIN_OR_MAX; -use clippy_utils::diagnostics::span_lint_and_sugg; - use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt}; +use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::Span; +use rustc_span::{Span, sym}; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -21,26 +20,30 @@ pub(super) fn check<'tcx>( ) { let typeck_results = cx.typeck_results(); let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results); - if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) - && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id) + && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id)) { - let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { - return; - }; + if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv) + && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg) + { + let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else { + return; + }; - lint(cx, expr, name, recv.span, arg.span, ord); - } else if let Some(extrema) = detect_extrema(cx, recv) { - let ord = match extrema { - Extrema::Minimum => Ordering::Less, - Extrema::Maximum => Ordering::Greater, - }; - lint(cx, expr, name, recv.span, arg.span, ord); - } else if let Some(extrema) = detect_extrema(cx, arg) { - let ord = match extrema { - Extrema::Minimum => Ordering::Greater, - Extrema::Maximum => Ordering::Less, - }; - lint(cx, expr, name, recv.span, arg.span, ord); + lint(cx, expr, name, recv.span, arg.span, ord); + } else if let Some(extrema) = detect_extrema(cx, recv) { + let ord = match extrema { + Extrema::Minimum => Ordering::Less, + Extrema::Maximum => Ordering::Greater, + }; + lint(cx, expr, name, recv.span, arg.span, ord); + } else if let Some(extrema) = detect_extrema(cx, arg) { + let ord = match extrema { + Extrema::Minimum => Ordering::Greater, + Extrema::Maximum => Ordering::Less, + }; + lint(cx, expr, name, recv.span, arg.span, ord); + } } } diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs index c14a87c15341..dc50717112d8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs @@ -8,8 +8,8 @@ use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath}; use rustc_lint::LateContext; use rustc_span::symbol::sym; -use super::utils::get_last_chain_binding_hir_id; use super::UNNECESSARY_RESULT_MAP_OR_ELSE; +use super::utils::get_last_chain_binding_hir_id; fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) { let msg = "unused \"map closure\" when calling `Result::map_or_else` value"; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index bf7555a29e22..cfa1fdb81372 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -2,12 +2,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, - return_ty, + fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, return_ty, }; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -20,7 +19,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::{Obligation, ObligationCause}; @@ -717,7 +716,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx // check that: // 1. This is a method with only one argument that doesn't come from a trait. // 2. That it has `Borrow` in its generic predicates. -// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, BTreeSet`, `BTreeMap`). +// 3. `Self` is a std "map type" (ie `HashSet`, `HashMap`, `BTreeSet`, `BTreeMap`). fn check_borrow_predicate<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if let ExprKind::MethodCall(_, caller, &[arg], _) = expr.kind && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs index ee5177d1ffaa..6c2ae9cc6bff 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::AdtDef; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use crate::loops::UNUSED_ENUMERATE_INDEX; diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index ebad4ae6ee95..eafe7486bb05 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -9,7 +9,7 @@ use rustc_hir::{self as hir, LangItem}; use rustc_lint::LateContext; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use core::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs index fe860e5ae260..4e33dc1df54d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/utils.rs +++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs @@ -1,12 +1,12 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{get_parent_expr, path_to_local_id, usage}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, Mutability, Pat, QPath, Stmt, StmtKind}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; pub(super) fn derefs_to_slice<'tcx>( cx: &LateContext<'tcx>, diff --git a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs index 3e271a606115..5ea4ada128a2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs +++ b/src/tools/clippy/clippy_lints/src/methods/vec_resize_to_zero.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use super::VEC_RESIZE_TO_ZERO; diff --git a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs index 9b64cc7589cc..b5f34a9be2e2 100644 --- a/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs +++ b/src/tools/clippy/clippy_lints/src/methods/waker_clone_wake.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet_with_applicability; use clippy_utils::is_trait_method; +use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs index c83e5198c274..a99e21d938c6 100644 --- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs +++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::is_from_proc_macro; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_item, walk_trait_item, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item}; use rustc_hir::{GenericParamKind, HirId, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem, UsePath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs index a9aafe7ed56c..408dbef9cb16 100644 --- a/src/tools/clippy/clippy_lints/src/misc.rs +++ b/src/tools/clippy/clippy_lints/src/misc.rs @@ -2,11 +2,12 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir use clippy_utils::source::{snippet, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::{ - fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, last_path_segment, - SpanlessEq, + SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, + last_path_segment, }; use rustc_errors::Applicability; use rustc_hir::def::Res; +use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::intravisit::FnKind; use rustc_hir::{ BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind, @@ -14,8 +15,8 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use crate::ref_patterns::REF_PATTERNS; @@ -80,6 +81,45 @@ declare_clippy_lint! { "using a binding which is prefixed with an underscore" } +declare_clippy_lint! { + /// ### What it does + /// Checks for the use of item with a single leading + /// underscore. + /// + /// ### Why is this bad? + /// A single leading underscore is usually used to indicate + /// that a item will not be used. Using such a item breaks this + /// expectation. + /// + /// ### Example + /// ```no_run + /// fn _foo() {} + /// + /// struct _FooStruct {} + /// + /// fn main() { + /// _foo(); + /// let _ = _FooStruct{}; + /// } + /// ``` + /// + /// Use instead: + /// ```no_run + /// fn foo() {} + /// + /// struct FooStruct {} + /// + /// fn main() { + /// foo(); + /// let _ = FooStruct{}; + /// } + /// ``` + #[clippy::version = "pre 1.29.0"] + pub USED_UNDERSCORE_ITEMS, + pedantic, + "using a item which is prefixed with an underscore" +} + declare_clippy_lint! { /// ### What it does /// Checks for the use of short circuit boolean conditions as @@ -104,6 +144,7 @@ declare_clippy_lint! { declare_lint_pass!(LintPass => [ TOPLEVEL_REF_ARG, USED_UNDERSCORE_BINDING, + USED_UNDERSCORE_ITEMS, SHORT_CIRCUIT_STATEMENT, ]); @@ -205,51 +246,104 @@ impl<'tcx> LateLintPass<'tcx> for LintPass { { return; } - let (definition_hir_id, ident) = match expr.kind { - ExprKind::Path(ref qpath) => { - if let QPath::Resolved(None, path) = qpath - && let Res::Local(id) = path.res - && is_used(cx, expr) - { - (id, last_path_segment(qpath).ident) - } else { - return; - } - }, - ExprKind::Field(recv, ident) => { - if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() - && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) - && let Some(local_did) = field.did.as_local() - && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() - { - (cx.tcx.local_def_id_to_hir_id(local_did), ident) - } else { - return; - } - }, - _ => return, - }; - let name = ident.name.as_str(); - if name.starts_with('_') - && !name.starts_with("__") - && let definition_span = cx.tcx.hir().span(definition_hir_id) - && !definition_span.from_expansion() - && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) - { - span_lint_and_then( - cx, - USED_UNDERSCORE_BINDING, - expr.span, - format!( - "used binding `{name}` which is prefixed with an underscore. A leading \ - underscore signals that a binding will not be used" - ), - |diag| { - diag.span_note(definition_span, format!("`{name}` is defined here")); - }, - ); - } + used_underscore_binding(cx, expr); + used_underscore_items(cx, expr); + } +} + +fn used_underscore_items<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (def_id, ident) = match expr.kind { + ExprKind::Call(func, ..) => { + if let ExprKind::Path(QPath::Resolved(.., path)) = func.kind + && let Some(last_segment) = path.segments.last() + && let Res::Def(_, def_id) = last_segment.res + { + (def_id, last_segment.ident) + } else { + return; + } + }, + ExprKind::MethodCall(path, ..) => { + if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) { + (def_id, path.ident) + } else { + return; + } + }, + ExprKind::Struct(QPath::Resolved(_, path), ..) => { + if let Some(last_segment) = path.segments.last() + && let Res::Def(_, def_id) = last_segment.res + { + (def_id, last_segment.ident) + } else { + return; + } + }, + _ => return, + }; + + let name = ident.name.as_str(); + let definition_span = cx.tcx.def_span(def_id); + if name.starts_with('_') + && !name.starts_with("__") + && !definition_span.from_expansion() + && def_id.krate == LOCAL_CRATE + { + span_lint_and_then( + cx, + USED_UNDERSCORE_ITEMS, + expr.span, + "used underscore-prefixed item".to_string(), + |diag| { + diag.span_note(definition_span, "item is defined here".to_string()); + }, + ); + } +} + +fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (definition_hir_id, ident) = match expr.kind { + ExprKind::Path(ref qpath) => { + if let QPath::Resolved(None, path) = qpath + && let Res::Local(id) = path.res + && is_used(cx, expr) + { + (id, last_path_segment(qpath).ident) + } else { + return; + } + }, + ExprKind::Field(recv, ident) => { + if let Some(adt_def) = cx.typeck_results().expr_ty_adjusted(recv).ty_adt_def() + && let Some(field) = adt_def.all_fields().find(|field| field.name == ident.name) + && let Some(local_did) = field.did.as_local() + && !cx.tcx.type_of(field.did).skip_binder().is_phantom_data() + { + (cx.tcx.local_def_id_to_hir_id(local_did), ident) + } else { + return; + } + }, + _ => return, + }; + + let name = ident.name.as_str(); + if name.starts_with('_') + && !name.starts_with("__") + && let definition_span = cx.tcx.hir().span(definition_hir_id) + && !definition_span.from_expansion() + && !fulfill_or_allowed(cx, USED_UNDERSCORE_BINDING, [expr.hir_id, definition_hir_id]) + { + span_lint_and_then( + cx, + USED_UNDERSCORE_BINDING, + expr.span, + "used underscore-prefixed binding".to_string(), + |diag| { + diag.span_note(definition_span, "binding is defined here".to_string()); + }, + ); } } diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs index a9ea11f4c2b0..86348f04600b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs +++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_test; -use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn}; +use clippy_utils::macros::{PanicExpn, find_assert_args, find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index 94c91b095171..b40d7eba15e5 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -1,7 +1,7 @@ use std::mem; use std::ops::ControlFlow; -use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_expr_without_closures; @@ -14,7 +14,7 @@ use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs index 859afe1b9636..eea0459e026e 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; @@ -11,8 +11,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs index 954216038fb5..c2f524a63531 100644 --- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs +++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs @@ -1,12 +1,12 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::macro_backtrace; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::source::snippet; use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks}; use rustc_errors::Applicability; -use rustc_hir::{intravisit, Expr, ExprKind}; +use rustc_hir::{Expr, ExprKind, intravisit}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::sym::{self, thread_local_macro}; diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs index 2166b0fe5a06..64fc1a8a1a58 100644 --- a/src/tools/clippy/clippy_lints/src/missing_doc.rs +++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs @@ -17,7 +17,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::Visibility; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 77595b121aad..fc01b6753f1c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -3,7 +3,7 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_path_lang_item; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::{for_each_expr, Visitable}; +use clippy_utils::visitors::{Visitable, for_each_expr}; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::{DefKind, Res}; @@ -13,7 +13,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{Ty, TypeckResults}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 33a14d8b7fed..d342be4545ce 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -3,7 +3,7 @@ use rustc_ast::ast; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index 0b3769ecb7cc..d333b71edb1a 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; @@ -134,6 +135,11 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> { } fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) { + if let Some(macro_call) = root_macro_call_first_node(self.cx, e) { + if self.cx.tcx.item_name(macro_call.def_id).as_str() == "todo" { + return; + } + } span_lint(self.cx, DIVERGING_SUB_EXPRESSION, e.span, "sub-expression diverges"); } } diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs index 0bde0da3cd81..12bcc608174f 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::visitors::{for_each_expr, Descend, Visitable}; +use clippy_utils::visitors::{Descend, Visitable, for_each_expr}; use core::ops::ControlFlow::Continue; use hir::def::{DefKind, Res}; use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp}; @@ -153,19 +153,16 @@ fn collect_unsafe_exprs<'tcx>( ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Assign(lhs, rhs, _) => { if matches!( lhs.kind, - ExprKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::Def( - DefKind::Static { - mutability: Mutability::Mut, - .. - }, - _ - ), - .. - } - )) + ExprKind::Path(QPath::Resolved(_, hir::Path { + res: Res::Def( + DefKind::Static { + mutability: Mutability::Mut, + .. + }, + _ + ), + .. + })) ) { unsafe_ops.push(("modification of a mutable static occurs here", expr.span)); collect_unsafe_exprs(cx, rhs, unsafe_ops); diff --git a/src/tools/clippy/clippy_lints/src/mut_key.rs b/src/tools/clippy/clippy_lints/src/mut_key.rs index f52b3a6a5a16..8118c14bd4a2 100644 --- a/src/tools/clippy/clippy_lints/src/mut_key.rs +++ b/src/tools/clippy/clippy_lints/src/mut_key.rs @@ -6,9 +6,9 @@ use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; -use rustc_span::Span; use std::iter; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs index e92ba93942ef..785bf70a3ec9 100644 --- a/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs +++ b/src/tools/clippy/clippy_lints/src/mutable_debug_assertion.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; diff --git a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs index 60c44382059a..3c47d0edfdc5 100644 --- a/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/clippy_lints/src/needless_arbitrary_self_type.rs @@ -3,8 +3,8 @@ use rustc_ast::ast::{BindingMode, ByRef, Lifetime, Mutability, Param, PatKind, P use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Span; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs index df155a7a4125..2eacd6875d6b 100644 --- a/src/tools/clippy/clippy_lints/src/needless_bool.rs +++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs @@ -2,16 +2,16 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, is_receiver_of_method_call, - peel_blocks, peel_blocks_with_stmt, span_extract_comment, SpanlessEq, + SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, + is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_extract_comment, }; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs index 32e7fde03b2c..f6db12ed84e8 100644 --- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs +++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs @@ -1,10 +1,10 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once}; use clippy_utils::source::snippet_with_context; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode}; +use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs index 6390e51f916b..b54eb164e81d 100644 --- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs +++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs @@ -1,9 +1,9 @@ use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_trait_method; diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs index 62e41088f370..bb44ff37b203 100644 --- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs +++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs @@ -26,7 +26,7 @@ declare_clippy_lint! { /// /// // or choose alternative bounds for `T` so that it can be unsized /// ``` - #[clippy::version = "1.79.0"] + #[clippy::version = "1.81.0"] pub NEEDLESS_MAYBE_SIZED, suspicious, "a `?Sized` bound that is unusable due to a `Sized` requirement" diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index d543fd467abe..19cbf5959084 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -17,9 +17,9 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt, UpvarId, UpvarPath}; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::Span; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index addb4b1aee80..0775d7abdbb3 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_self; use clippy_utils::ptr::get_spans; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::{ implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item, }; @@ -19,7 +19,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitableExt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::misc::type_allowed_to_implement_copy; @@ -129,7 +129,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { }) .collect::>(); - // Collect moved variables and spans which will need dereferencings from the + // Collect moved variables and spans which will need dereferencing from the // function body. let MovedVariablesCtxt { moved_vars } = { let mut ctx = MovedVariablesCtxt::default(); @@ -148,12 +148,13 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { return; } - // Ignore `self`s. - if idx == 0 { - if let PatKind::Binding(.., ident, _) = arg.pat.kind { - if ident.name == kw::SelfLower { - continue; - } + // Ignore `self`s and params whose variable name starts with an underscore + if let PatKind::Binding(.., ident, _) = arg.pat.kind { + if idx == 0 && ident.name == kw::SelfLower { + continue; + } + if ident.name.as_str().starts_with('_') { + continue; } } @@ -181,14 +182,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { && !is_copy(cx, ty) && ty.is_sized(cx.tcx, cx.param_env) && !allowed_traits.iter().any(|&t| { - implements_trait_with_env_from_iter( - cx.tcx, - cx.param_env, - ty, - t, - None, - [Option::>::None], - ) + implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::< + ty::GenericArg<'tcx>, + >::None]) }) && !implements_borrow_trait && !all_borrowable_trait @@ -207,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { ) .is_ok() { - diag.span_help(span, "consider marking this type as `Copy`"); + diag.span_help(span, "or consider marking this type as `Copy`"); } } } diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index b181791699a5..392cfcb813e8 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -7,8 +7,8 @@ use clippy_utils::{ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, - Stmt, StmtKind, UnsafeSource, + BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind, + UnsafeSource, is_range_literal, }; use rustc_infer::infer::TyCtxtInferExt as _; use rustc_lint::{LateContext, LateLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 25f547f1a43c..5e20b4064260 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -4,7 +4,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::macros::macro_backtrace; -use clippy_utils::ty::{implements_trait, InteriorMut}; +use clippy_utils::ty::{InteriorMut, implements_trait}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{ @@ -15,7 +15,7 @@ use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId}; use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, sym}; use rustc_target::abi::VariantIdx; // FIXME: this is a correctness problem but there's no suitable diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs index 832518d2d35b..d85032e9eee8 100644 --- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs +++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs @@ -3,12 +3,12 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use rustc_ast::ast::{ self, Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, Pat, PatKind, }; -use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor}; +use rustc_ast::visit::{Visitor, walk_block, walk_expr, walk_pat}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::{Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index cfc15d927158..3f156aa55104 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs new file mode 100644 index 000000000000..90a9f2e994b8 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs @@ -0,0 +1,143 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_ast::ast::BinOpKind; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for conversions from `NonZero` types to regular integer types, + /// and suggests using `NonZero` types for the target as well. + /// + /// ### Why is this bad? + /// Converting from `NonZero` types to regular integer types and then back to `NonZero` + /// types is less efficient and loses the type-safety guarantees provided by `NonZero` types. + /// Using `NonZero` types consistently can lead to more optimized code and prevent + /// certain classes of errors related to zero values. + /// + /// ### Example + /// ```no_run + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Bad: Converting NonZeroU32 to u64 unnecessarily + /// let r1 = x / u64::from(y.get()); + /// let r2 = x % u64::from(y.get()); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// use std::num::{NonZeroU32, NonZeroU64}; + /// + /// fn example(x: u64, y: NonZeroU32) { + /// // Good: Preserving the NonZero property + /// let r1 = x / NonZeroU64::from(y); + /// let r2 = x % NonZeroU64::from(y); + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub NON_ZERO_SUGGESTIONS, + restriction, + "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions" +} + +declare_lint_pass!(NonZeroSuggestions => [NON_ZERO_SUGGESTIONS]); + +impl<'tcx> LateLintPass<'tcx> for NonZeroSuggestions { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Binary(op, _, rhs) = expr.kind + && matches!(op.node, BinOpKind::Div | BinOpKind::Rem) + { + check_non_zero_conversion(cx, rhs, Applicability::MachineApplicable); + } else { + // Check if the parent expression is a binary operation + let parent_is_binary = cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, node)| { + matches!(node, rustc_hir::Node::Expr(parent_expr) if matches!(parent_expr.kind, ExprKind::Binary(..))) + }); + + if !parent_is_binary { + check_non_zero_conversion(cx, expr, Applicability::MaybeIncorrect); + } + } + } +} + +fn check_non_zero_conversion(cx: &LateContext<'_>, expr: &Expr<'_>, applicability: Applicability) { + // Check if the expression is a function call with one argument + if let ExprKind::Call(func, [arg]) = expr.kind + && let ExprKind::Path(qpath) = &func.kind + && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() + && let ExprKind::MethodCall(rcv_path, receiver, _, _) = &arg.kind + && rcv_path.ident.name.as_str() == "get" + { + let fn_name = cx.tcx.item_name(def_id); + let target_ty = cx.typeck_results().expr_ty(expr); + let receiver_ty = cx.typeck_results().expr_ty(receiver); + + // Check if the receiver type is a NonZero type + if let ty::Adt(adt_def, _) = receiver_ty.kind() + && adt_def.is_struct() + && cx.tcx.get_diagnostic_name(adt_def.did()) == Some(sym::NonZero) + { + if let Some(target_non_zero_type) = get_target_non_zero_type(target_ty) { + let arg_snippet = get_arg_snippet(cx, arg, rcv_path); + suggest_non_zero_conversion(cx, expr, fn_name, target_non_zero_type, &arg_snippet, applicability); + } + } + } +} + +fn get_arg_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, rcv_path: &rustc_hir::PathSegment<'_>) -> String { + let arg_snippet = snippet(cx, arg.span, ".."); + if let Some(index) = arg_snippet.rfind(&format!(".{}", rcv_path.ident.name)) { + arg_snippet[..index].trim().to_string() + } else { + arg_snippet.to_string() + } +} + +fn suggest_non_zero_conversion( + cx: &LateContext<'_>, + expr: &Expr<'_>, + fn_name: rustc_span::Symbol, + target_non_zero_type: &str, + arg_snippet: &str, + applicability: Applicability, +) { + let suggestion = format!("{target_non_zero_type}::{fn_name}({arg_snippet})"); + span_lint_and_sugg( + cx, + NON_ZERO_SUGGESTIONS, + expr.span, + format!("consider using `{target_non_zero_type}::{fn_name}()` for more efficient and type-safe conversion"), + "replace with", + suggestion, + applicability, + ); +} + +fn get_target_non_zero_type(ty: Ty<'_>) -> Option<&'static str> { + match ty.kind() { + ty::Uint(uint_ty) => Some(match uint_ty { + ty::UintTy::U8 => "NonZeroU8", + ty::UintTy::U16 => "NonZeroU16", + ty::UintTy::U32 => "NonZeroU32", + ty::UintTy::U64 => "NonZeroU64", + ty::UintTy::U128 => "NonZeroU128", + ty::UintTy::Usize => "NonZeroUsize", + }), + ty::Int(int_ty) => Some(match int_ty { + ty::IntTy::I8 => "NonZeroI8", + ty::IntTy::I16 => "NonZeroI16", + ty::IntTy::I32 => "NonZeroI32", + ty::IntTy::I64 => "NonZeroI64", + ty::IntTy::I128 => "NonZeroI128", + ty::IntTy::Isize => "NonZeroIsize", + }), + _ => None, + } +} diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index 51ba29d7389b..a13391a59457 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,5 +1,5 @@ -use clippy_config::types::MacroMatcher; use clippy_config::Conf; +use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::{SourceText, SpanRangeExt}; use rustc_ast::ast; @@ -8,8 +8,8 @@ use rustc_errors::Applicability; use rustc_hir::def_id::DefId; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::Span; +use rustc_span::hygiene::{ExpnKind, MacroKind}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs index aadd729f32a4..372128ac16f0 100644 --- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs @@ -9,8 +9,8 @@ use rustc_hir::{Body, Expr, ExprKind, HirId, ImplItem, ImplItemKind, Node, PatKi use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{self, ConstKind, EarlyBinder, GenericArgKind, GenericArgsRef}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{kw, Ident}; use rustc_span::Span; +use rustc_span::symbol::{Ident, kw}; use std::iter; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs index a0de5ea711ca..dbc9948eeedc 100644 --- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -2,7 +2,7 @@ use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use clippy_utils::comparisons::{normalize_comparison, Rel}; +use clippy_utils::comparisons::{Rel, normalize_comparison}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::snippet; diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs index c13175243962..5d94cfab3b02 100644 --- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs @@ -7,12 +7,12 @@ 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::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; +use clippy_utils::SpanlessEq; use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::source::snippet; -use clippy_utils::SpanlessEq; use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS}; diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs index be97ad389bf6..34f7dbea84e4 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs @@ -6,7 +6,8 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_span::{sym, source_map::Spanned}; +use rustc_span::source_map::Spanned; +use rustc_span::sym; use super::FLOAT_EQUALITY_WITHOUT_ABS; 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 ae02d22512e3..6d9e75f51d66 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 @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::sugg::Sugg; use clippy_utils::{ - can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, - peel_blocks, peel_hir_expr_while, CaptureKind, + CaptureKind, can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, + is_res_lang_ctor, peel_blocks, peel_hir_expr_while, }; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::Res; use rustc_hir::{Arm, BindingMode, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index 381975199d28..eebc62e2a5a9 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -1,15 +1,15 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; +use clippy_utils::{is_inside_always_const_context, return_ty}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_hir::intravisit::FnKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -68,10 +68,12 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; - if matches!( - cx.tcx.item_name(macro_call.def_id).as_str(), - "panic" | "assert" | "assert_eq" | "assert_ne" - ) { + if !is_inside_always_const_context(cx.tcx, e.hir_id) + && matches!( + cx.tcx.item_name(macro_call.def_id).as_str(), + "panic" | "assert" | "assert_eq" | "assert_ne" + ) + { panics.push(macro_call.span); ControlFlow::Continue(Descend::No) } else { diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs index 4eefd0065f65..fa5b02a5a41b 100644 --- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs +++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; -use clippy_utils::is_in_test; use clippy_utils::macros::{is_panic, root_macro_call_first_node}; -use rustc_hir::Expr; +use clippy_utils::{is_in_test, match_def_path, paths}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -95,10 +96,49 @@ impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]) impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let Some(macro_call) = root_macro_call_first_node(cx, expr) else { - return; - }; - if is_panic(cx, macro_call.def_id) { + if let Some(macro_call) = root_macro_call_first_node(cx, expr) { + if is_panic(cx, macro_call.def_id) { + if cx.tcx.hir().is_inside_const_context(expr.hir_id) + || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) + { + return; + } + + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { + span_lint( + cx, + UNIMPLEMENTED, + macro_call.span, + "`unimplemented` should not be present in production code", + ); + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, + } + } else if let ExprKind::Call(func, [_]) = expr.kind + && let ExprKind::Path(QPath::Resolved(None, expr_path)) = func.kind + && let Res::Def(DefKind::Fn, def_id) = expr_path.res + && match_def_path(cx, def_id, &paths::PANIC_ANY) + { if cx.tcx.hir().is_inside_const_context(expr.hir_id) || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id) { @@ -108,32 +148,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { span_lint( cx, PANIC, - macro_call.span, - "`panic` should not be present in production code", + expr.span, + "`panic_any` should not be present in production code", ); return; } - match cx.tcx.item_name(macro_call.def_id).as_str() { - "todo" => { - span_lint( - cx, - TODO, - macro_call.span, - "`todo` should not be present in production code", - ); - }, - "unimplemented" => { - span_lint( - cx, - UNIMPLEMENTED, - macro_call.span, - "`unimplemented` should not be present in production code", - ); - }, - "unreachable" => { - span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); - }, - _ => {}, - } } } diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs index 5ca244f0141c..75d8c09f2b08 100644 --- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs +++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs @@ -18,7 +18,7 @@ use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::{self, RegionKind, TyCtxt}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index d7fa48c1e38d..1b9a5a443829 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::path_to_local_id; -use clippy_utils::source::{snippet, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet}; use clippy_utils::ty::is_type_diagnostic_item; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -9,7 +9,7 @@ use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, LetStmt, PatKind, QPa use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs index c1296b04387a..42fbba8ef6dc 100644 --- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs +++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::{ - intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, + Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind, intravisit, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs b/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs new file mode 100644 index 000000000000..385f634a1505 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/pointers_in_nomem_asm_block.rs @@ -0,0 +1,88 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use rustc_ast::InlineAsmOptions; +use rustc_hir::{Expr, ExprKind, InlineAsm, InlineAsmOperand}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks if any pointer is being passed to an asm! block with `nomem` option. + /// + /// ### Why is this bad? + /// `nomem` forbids any reads or writes to memory and passing a pointer suggests + /// that either of those will happen. + /// + /// ### Example + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nomem, nostack)); } + /// } + /// ``` + /// Use instead: + /// ```no_run + /// fn f(p: *mut u32) { + /// unsafe { core::arch::asm!("mov [{p}], 42", p = in(reg) p, options(nostack)); } + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub POINTERS_IN_NOMEM_ASM_BLOCK, + suspicious, + "pointers in nomem asm block" +} + +declare_lint_pass!(PointersInNomemAsmBlock => [POINTERS_IN_NOMEM_ASM_BLOCK]); + +impl<'tcx> LateLintPass<'tcx> for PointersInNomemAsmBlock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { + if let ExprKind::InlineAsm(asm) = &expr.kind { + check_asm(cx, asm); + } + } +} + +fn check_asm(cx: &LateContext<'_>, asm: &InlineAsm<'_>) { + if !asm.options.contains(InlineAsmOptions::NOMEM) { + return; + } + + let spans = asm + .operands + .iter() + .filter(|(op, _span)| has_in_operand_pointer(cx, op)) + .map(|(_op, span)| *span) + .collect::>(); + + if spans.is_empty() { + return; + } + + span_lint_and_then( + cx, + POINTERS_IN_NOMEM_ASM_BLOCK, + spans, + "passing pointers to nomem asm block", + additional_notes, + ); +} + +fn has_in_operand_pointer(cx: &LateContext<'_>, asm_op: &InlineAsmOperand<'_>) -> bool { + let asm_in_expr = match asm_op { + InlineAsmOperand::SymStatic { .. } + | InlineAsmOperand::Out { .. } + | InlineAsmOperand::Const { .. } + | InlineAsmOperand::SymFn { .. } + | InlineAsmOperand::Label { .. } => return false, + InlineAsmOperand::SplitInOut { in_expr, .. } => in_expr, + InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => expr, + }; + + // This checks for raw ptrs, refs and function pointers - the last one + // also technically counts as reading memory. + cx.typeck_results().expr_ty(asm_in_expr).is_any_ptr() +} + +fn additional_notes(diag: &mut rustc_errors::Diag<'_, ()>) { + diag.note("`nomem` means that no memory write or read happens inside the asm! block"); + diag.note("if this is intentional and no pointers are read or written to, consider allowing the lint"); +} diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs index 125f694996c1..807636bb642b 100644 --- a/src/tools/clippy/clippy_lints/src/ptr.rs +++ b/src/tools/clippy/clippy_lints/src/ptr.rs @@ -1,13 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::ty::expr_sig; use clippy_utils::visitors::contains_unsafe_block; use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local}; use hir::LifetimeName; use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::def_id::DefId; use rustc_hir::hir_id::{HirId, HirIdMap}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{ self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind, ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, Safety, TraitFn, TraitItem, TraitItemKind, TyKind, @@ -19,7 +17,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Binder, ClauseKind, ExistentialPredicate, List, PredicateKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_target::spec::abi::Abi; use rustc_trait_selection::infer::InferCtxtExt as _; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; @@ -34,7 +32,7 @@ declare_clippy_lint! { /// with the appropriate `.to_owned()`/`to_string()` calls. /// /// ### Why is this bad? - /// Requiring the argument to be of the specific size + /// Requiring the argument to be of the specific type /// makes the function less useful for no benefit; slices in the form of `&[T]` /// or `&str` usually suffice and can be obtained from other types, too. /// @@ -323,7 +321,6 @@ struct PtrArg<'tcx> { idx: usize, emission_id: HirId, span: Span, - ty_did: DefId, ty_name: Symbol, method_renames: &'static [(&'static str, &'static str)], ref_prefix: RefPrefix, @@ -411,7 +408,6 @@ impl<'tcx> DerefTy<'tcx> { } } -#[expect(clippy::too_many_lines)] fn check_fn_args<'cx, 'tcx: 'cx>( cx: &'cx LateContext<'tcx>, fn_sig: ty::FnSig<'tcx>, @@ -514,7 +510,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>( idx: i, emission_id, span: hir_ty.span, - ty_did: adt.did(), ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { lt: *lt, mutability }, @@ -610,65 +605,50 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[ set_skip_flag(); } }, - Some((Node::Expr(e), child_id)) => match e.kind { - ExprKind::Call(f, expr_args) => { - let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0); - if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| { - match *ty.skip_binder().peel_refs().kind() { - ty::Dynamic(preds, _, _) => !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds), - ty::Param(_) => true, - ty::Adt(def, _) => def.did() == args.ty_did, - _ => false, - } - }) { - // Passed to a function taking the non-dereferenced type. - set_skip_flag(); - } - }, - ExprKind::MethodCall(name, self_arg, expr_args, _) => { - let i = iter::once(self_arg) - .chain(expr_args.iter()) - .position(|arg| arg.hir_id == child_id) - .unwrap_or(0); - if i == 0 { - // Check if the method can be renamed. - let name = name.ident.as_str(); - if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) { - result.replacements.push(PtrArgReplacement { - expr_span: e.span, - self_span: self_arg.span, - replacement, - }); - return; - } - } + Some((Node::Expr(use_expr), child_id)) => { + if let ExprKind::Index(e, ..) = use_expr.kind + && e.hir_id == child_id + { + // Indexing works with both owned and its dereferenced type + return; + } - let Some(id) = self.cx.typeck_results().type_dependent_def_id(e.hir_id) else { - set_skip_flag(); + if let ExprKind::MethodCall(name, receiver, ..) = use_expr.kind + && receiver.hir_id == child_id + { + let name = name.ident.as_str(); + + // Check if the method can be renamed. + if let Some((_, replacement)) = args.method_renames.iter().find(|&&(x, _)| x == name) { + result.replacements.push(PtrArgReplacement { + expr_span: use_expr.span, + self_span: receiver.span, + replacement, + }); return; - }; - - match *self.cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i] - .peel_refs() - .kind() - { - ty::Dynamic(preds, _, _) if !matches_preds(self.cx, args.deref_ty.ty(self.cx), preds) => { - set_skip_flag(); - }, - ty::Param(_) => { - set_skip_flag(); - }, - // If the types match check for methods which exist on both types. e.g. `Vec::len` and - // `slice::len` - ty::Adt(def, _) if def.did() == args.ty_did && !is_allowed_vec_method(self.cx, e) => { - set_skip_flag(); - }, - _ => (), } - }, - // Indexing is fine for currently supported types. - ExprKind::Index(e, _, _) if e.hir_id == child_id => (), - _ => set_skip_flag(), + + // Some methods exist on both `[T]` and `Vec`, such as `len`, where the receiver type + // doesn't coerce to a slice and our adjusted type check below isn't enough, + // but it would still be valid to call with a slice + if is_allowed_vec_method(self.cx, use_expr) { + return; + } + } + + let deref_ty = args.deref_ty.ty(self.cx); + let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs(); + if adjusted_ty == deref_ty { + return; + } + + if let ty::Dynamic(preds, ..) = adjusted_ty.kind() + && matches_preds(self.cx, deref_ty, preds) + { + return; + } + + set_skip_flag(); }, _ => set_skip_flag(), } diff --git a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs index 77b707567e4b..db03657c9af4 100644 --- a/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs +++ b/src/tools/clippy/clippy_lints/src/pub_underscore_fields.rs @@ -1,5 +1,5 @@ -use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_config::Conf; +use clippy_config::types::PubUnderscoreFieldsBehaviour; use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_path_lang_item; diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index e1e3ded2c795..aa9a9001afb7 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -1,8 +1,8 @@ use crate::manual_let_else::MANUAL_LET_ELSE; use crate::question_mark_used::QUESTION_MARK_USED; +use clippy_config::Conf; use clippy_config::msrvs::Msrv; use clippy_config::types::MatchLintBehaviour; -use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; @@ -12,8 +12,8 @@ use clippy_utils::{ span_contains_comment, }; use rustc_errors::Applicability; -use rustc_hir::def::Res; use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::def::Res; use rustc_hir::{ BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt, StmtKind, diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 81189fe517c0..21cd33672624 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local}; use rustc_ast::ast::RangeLimits; @@ -11,8 +11,8 @@ use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::source_map::Spanned; use rustc_span::Span; +use rustc_span::source_map::Spanned; use std::cmp::Ordering; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs index d0b45b59526f..e877f5d6ed43 100644 --- a/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/rc_clone_in_vec_init.rs @@ -8,7 +8,7 @@ use rustc_hir::{Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, Symbol}; +use rustc_span::{Span, Symbol, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs index 7f4735c6a889..6bd68dd4109d 100644 --- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs +++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::get_enclosing_block; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def::Res; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index 4e24ddad83a5..b9e0106fc86b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,17 +1,17 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; -use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap}; +use clippy_utils::fn_has_unsatisfiable_preds; +use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::source::SpanRangeExt; use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth}; -use clippy_utils::fn_has_unsatisfiable_preds; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; -use rustc_hir::{def_id, Body, FnDecl, LangItem}; +use rustc_hir::{Body, FnDecl, LangItem, def_id}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::mir; use rustc_middle::ty::{self, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; macro_rules! unwrap_or_continue { ($x:expr) => { @@ -349,14 +349,10 @@ fn visit_clone_usage(cloned: mir::Local, clone: mir::Local, mir: &mir::Body<'_>, local_use_locs: _, local_consume_or_mutate_locs: clone_consume_or_mutate_locs, }, - )) = visit_local_usage( - &[cloned, clone], - mir, - mir::Location { - block: bb, - statement_index: mir.basic_blocks[bb].statements.len(), - }, - ) + )) = visit_local_usage(&[cloned, clone], mir, mir::Location { + block: bb, + statement_index: mir.basic_blocks[bb].statements.len(), + }) .map(|mut vec| (vec.remove(0), vec.remove(0))) { CloneUsage { diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs index bad9b979203f..6930a01d48be 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs @@ -6,7 +6,7 @@ use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor}; use rustc_hir::{ - intravisit as hir_visit, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, + ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit, }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::hir::nested_filter; diff --git a/src/tools/clippy/clippy_lints/src/redundant_else.rs b/src/tools/clippy/clippy_lints/src/redundant_else.rs index 3bdf13dbbea6..6a1d40334e75 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_else.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_else.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_help; use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind}; -use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_expr}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs index 0e637538615e..d0dbff081f90 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs index d94ca5bc7ec2..4f46ca3c7150 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs @@ -9,8 +9,8 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::UpvarCapture; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::DesugaringKind; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs index d6e741dd974b..b27bb2e78afe 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind}; diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs index 2b4ef21fc48e..4bff37216eda 100644 --- a/src/tools/clippy/clippy_lints/src/reference.rs +++ b/src/tools/clippy/clippy_lints/src/reference.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{snippet_with_applicability, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index f6ef02b7c233..12cbdb854eff 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -3,7 +3,7 @@ use std::fmt::Display; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{def_path_def_ids, path_def_id, paths}; +use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths}; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind}; @@ -75,11 +75,14 @@ impl<'tcx> LateLintPass<'tcx> for Regex { // We don't use `match_def_path` here because that relies on matching the exact path, which changed // between regex 1.8 and 1.9 // - // `def_path_def_ids` will resolve through re-exports but is relatively heavy, so we only perform - // the operation once and store the results - let mut resolve = |path, kind| { - for id in def_path_def_ids(cx.tcx, path) { - self.definitions.insert(id, kind); + // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only + // perform the operation once and store the results + let regex_crates = find_crates(cx.tcx, sym!(regex)); + let mut resolve = |path: &[&str], kind: RegexKind| { + for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) { + if let Some(id) = res.opt_def_id() { + self.definitions.insert(id, kind); + } } }; diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs index 08de10f69b05..f54cafffb83c 100644 --- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs +++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs @@ -8,7 +8,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs index caf3fb8707da..6157adad059c 100644 --- a/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/reserve_after_initialization.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use clippy_utils::{is_from_proc_macro, path_to_local_id}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs index 5962e8be9594..42d9cf2c88c1 100644 --- a/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs +++ b/src/tools/clippy/clippy_lints/src/return_self_not_must_use.rs @@ -7,7 +7,7 @@ use rustc_hir::{Body, FnDecl, OwnerId, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs index 5ce091b7a7a3..3754fdddedfd 100644 --- a/src/tools/clippy/clippy_lints/src/returns.rs +++ b/src/tools/clippy/clippy_lints/src/returns.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; -use clippy_utils::source::{snippet_with_context, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr, for_each_unconsumed_temporary}; use clippy_utils::{ binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg, span_find_starting_semi, @@ -9,8 +9,8 @@ use clippy_utils::{ use core::ops::ControlFlow; use rustc_ast::NestedMetaItem; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::ResultErr; +use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt, StmtKind, @@ -21,7 +21,7 @@ use rustc_middle::ty::adjustment::Adjust; use rustc_middle::ty::{self, GenericArgKind, Ty}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, BytePos, Pos, Span}; +use rustc_span::{BytePos, Pos, Span, sym}; use std::borrow::Cow; use std::fmt::Display; @@ -389,10 +389,24 @@ fn check_final_expr<'tcx>( } }; - let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); - if borrows { - return; + if let Some(inner) = inner { + if for_each_unconsumed_temporary(cx, inner, |temporary_ty| { + if temporary_ty.has_significant_drop(cx.tcx, cx.param_env) + && temporary_ty + .walk() + .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static())) + { + ControlFlow::Break(()) + } else { + ControlFlow::Continue(()) + } + }) + .is_break() + { + return; + } } + if ret_span.from_expansion() { return; } diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs index 508f3ae6def0..8d31641d4836 100644 --- a/src/tools/clippy/clippy_lints/src/same_name_method.rs +++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs @@ -5,8 +5,8 @@ use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::AssocKind; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use std::collections::{BTreeMap, BTreeSet}; declare_clippy_lint! { @@ -62,13 +62,10 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod { && let TyKind::Path(QPath::Resolved(_, Path { res, .. })) = self_ty.kind { if !map.contains_key(res) { - map.insert( - *res, - ExistingName { - impl_methods: BTreeMap::new(), - trait_methods: BTreeMap::new(), - }, - ); + map.insert(*res, ExistingName { + impl_methods: BTreeMap::new(), + trait_methods: BTreeMap::new(), + }); } let existing_name = map.get_mut(res).unwrap(); diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index e6fe76493974..1185d67b1258 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -3,12 +3,12 @@ use std::ops::ControlFlow; use clippy_utils::diagnostics::span_lint; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{higher, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{SpanlessEq, higher, peel_hir_expr_while}; use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -42,7 +42,7 @@ declare_clippy_lint! { /// println!("inserted {value:?}"); /// } /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub SET_CONTAINS_OR_INSERT, nursery, "call to `::contains` followed by `::insert`" diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs index 979d6dc77aed..d1114cb29f7a 100644 --- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs +++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs @@ -4,13 +4,13 @@ use clippy_utils::{expr_or_init, get_attr, path_to_local, peel_hir_expr_unary}; use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{self as hir, HirId}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::{GenericArgKind, Ty}; use rustc_session::impl_lint_pass; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, sym}; use std::borrow::Cow; use std::collections::hash_map::Entry; diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index acf44a9bb5ab..c986c3e8aa6e 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use rustc_ast::node_id::{NodeId, NodeMap}; use rustc_ast::ptr::P; -use rustc_ast::visit::{walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; 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 04c16281ec4b..5129bbf26655 100644 --- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs +++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs @@ -2,11 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::sugg::Sugg; use clippy_utils::{ - get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, - path_to_local_id, SpanlessEq, + SpanlessEq, get_enclosing_block, is_integer_literal, is_path_diagnostic_item, path_to_local, path_to_local_id, }; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BindingMode, Block, Expr, ExprKind, HirId, PatKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index 44283a49e84b..8dd998587932 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use rustc_attr::{StabilityLevel, StableSince}; @@ -11,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index 7e211d64da14..ba2ddac2ec33 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -1,13 +1,13 @@ use std::ops::ControlFlow; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::macros::matching_root_macro_call; use clippy_utils::path_to_local_id; use clippy_utils::source::{snippet, str_literal_to_char_literal}; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use itertools::Itertools; use rustc_ast::{BinOpKind, LitKind}; use rustc_errors::Applicability; @@ -15,7 +15,7 @@ use rustc_hir::{Expr, ExprKind, PatKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does @@ -33,7 +33,7 @@ declare_clippy_lint! { /// ```no_run /// "Hello World!".trim_end_matches(['.', ',', '!', '?']); /// ``` - #[clippy::version = "1.80.0"] + #[clippy::version = "1.81.0"] pub MANUAL_PATTERN_CHAR_COMPARISON, style, "manual char comparison in string patterns" diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs index 6ca4ca000e9b..1fb82b66ab85 100644 --- a/src/tools/clippy/clippy_lints/src/strings.rs +++ b/src/tools/clippy/clippy_lints/src/strings.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_type_lang_item; use clippy_utils::{ - get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, method_calls, - peel_blocks, SpanlessEq, + SpanlessEq, get_expr_use_or_unification_node, get_parent_expr, is_lint_allowed, is_path_diagnostic_item, + method_calls, peel_blocks, }; use rustc_errors::Applicability; use rustc_hir::def_id::DefId; diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs index 1c1de805db0c..be32dc0aab0b 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs @@ -1,4 +1,4 @@ -use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter}; +use clippy_utils::ast_utils::{IdentIter, eq_id, is_useless_with_eq_exprs}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use core::ops::{Add, AddAssign}; @@ -7,9 +7,9 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index 744d6392e065..e9779d437d43 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use clippy_utils::visitors::for_each_expr_without_closures; -use clippy_utils::{binop_traits, trait_ref_of_method, BINOP_TRAITS, OP_ASSIGN_TRAITS}; +use clippy_utils::{BINOP_TRAITS, OP_ASSIGN_TRAITS, binop_traits, trait_ref_of_method}; use core::ops::ControlFlow; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 197011cde3a1..e05fa4095b8e 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -6,7 +6,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core}; use itertools::Itertools; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use crate::FxHashSet; use rustc_errors::Applicability; @@ -17,7 +17,7 @@ use rustc_middle::ty; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs index 20e9608a15dc..8c5cf93ab6e8 100644 --- a/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs +++ b/src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs @@ -5,7 +5,7 @@ use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span, SyntaxContext}; +use rustc_span::{Span, SyntaxContext, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs index 25d0a16e2ab4..3cd4fefffadf 100644 --- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs @@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs index dafe9e38818f..4f96a566b63c 100644 --- a/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs +++ b/src/tools/clippy/clippy_lints/src/to_digit_is_some.rs @@ -56,11 +56,13 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome { && let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id) && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id() - && match_def_path( - cx, - to_digits_def_id, - &["core", "char", "methods", "", "to_digit"], - ) + && match_def_path(cx, to_digits_def_id, &[ + "core", + "char", + "methods", + "", + "to_digit", + ]) { Some((false, char_arg, radix_arg)) } else { diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 2e87d36df312..00277593622a 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,8 +1,8 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt}; -use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash}; +use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs index da7906b11835..25fec9f688ca 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs @@ -19,8 +19,8 @@ mod useless_transmute; mod utils; mod wrong_transmute; -use clippy_config::msrvs::Msrv; use clippy_config::Conf; +use clippy_config::msrvs::Msrv; use clippy_utils::is_in_const_context; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; @@ -527,24 +527,44 @@ declare_clippy_lint! { /// Checks if transmute calls have all generics specified. /// /// ### Why is this bad? - /// If not set, some unexpected output type could be retrieved instead of the expected one, - /// potentially leading to invalid code. + /// If not, one or more unexpected types could be used during `transmute()`, potentially leading + /// to Undefined Behavior or other problems. /// - /// This is particularly dangerous in case a seemingly innocent/unrelated change can cause type - /// inference to start inferring a different type. E.g. the transmute is the tail expression of - /// an `if` branch, and a different branches type changes, causing the transmute to silently - /// have a different type, instead of a proper error. + /// This is particularly dangerous in case a seemingly innocent/unrelated change causes type + /// inference to result in a different type. For example, if `transmute()` is the tail + /// expression of an `if`-branch, and the `else`-branch type changes, the compiler may silently + /// infer a different type to be returned by `transmute()`. That is because the compiler is + /// free to change the inference of a type as long as that inference is technically correct, + /// regardless of the programmer's unknown expectation. + /// + /// Both type-parameters, the input- and the output-type, to any `transmute()` should + /// be given explicitly: Setting the input-type explicitly avoids confusion about what the + /// argument's type actually is. Setting the output-type explicitly avoids type-inference + /// to infer a technically correct yet unexpected type. /// /// ### Example /// ```no_run /// # unsafe { + /// // Avoid "naked" calls to `transmute()`! /// let x: i32 = std::mem::transmute([1u16, 2u16]); + /// + /// // `first_answers` is intended to transmute a slice of bool to a slice of u8. + /// // But the programmer forgot to index the first element of the outer slice, + /// // so we are actually transmuting from "pointers to slices" instead of + /// // transmuting from "a slice of bool", causing a nonsensical result. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute(the_answers); /// # } /// ``` /// Use instead: /// ```no_run /// # unsafe { /// let x = std::mem::transmute::<[u16; 2], i32>([1u16, 2u16]); + /// + /// // The explicit type parameters on `transmute()` makes the intention clear, + /// // and cause a type-error if the actual types don't match our expectation. + /// let the_answers: &[&[bool]] = &[&[true, false, true]]; + /// let first_answers: &[u8] = std::mem::transmute::<&[bool], &[u8]>(the_answers[0]); /// # } /// ``` #[clippy::version = "1.79.0"] diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs index ba8c7d6bfcb4..fca332dba401 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs @@ -6,8 +6,8 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Node}; use rustc_hir_typeck::cast::check_cast; use rustc_lint::LateContext; -use rustc_middle::ty::cast::CastKind; use rustc_middle::ty::Ty; +use rustc_middle::ty::cast::CastKind; /// Checks for `transmutes_expressible_as_ptr_casts` lint. /// Returns `true` if it's triggered, otherwise returns `false`. diff --git a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs index 35e938307660..f46e95b0b832 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/unsound_collection_transmute.rs @@ -1,5 +1,5 @@ -use super::utils::is_layout_incompatible; use super::UNSOUND_COLLECTION_TRANSMUTE; +use super::utils::is_layout_incompatible; use clippy_utils::diagnostics::span_lint; use rustc_hir::Expr; use rustc_lint::LateContext; diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs index 1d0de9327546..3da8a449a7c5 100644 --- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs +++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{is_from_proc_macro, path_to_local}; diff --git a/src/tools/clippy/clippy_lints/src/types/box_collection.rs b/src/tools/clippy/clippy_lints/src/types/box_collection.rs index 9ac73394548c..24fe4e08a5b4 100644 --- a/src/tools/clippy/clippy_lints/src/types/box_collection.rs +++ b/src/tools/clippy/clippy_lints/src/types/box_collection.rs @@ -3,7 +3,7 @@ use clippy_utils::{path_def_id, qpath_generic_tys}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use super::BOX_COLLECTION; diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs index 3a14927802b4..363aea8be72e 100644 --- a/src/tools/clippy/clippy_lints/src/types/mod.rs +++ b/src/tools/clippy/clippy_lints/src/types/mod.rs @@ -18,8 +18,8 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does @@ -261,8 +261,59 @@ declare_clippy_lint! { /// ### Example /// ```no_run /// # use std::rc::Rc; - /// struct Foo { - /// inner: Rc>>>, + /// struct PointMatrixContainer { + /// matrix: Rc>>>, + /// } + /// + /// fn main() { + /// let point_matrix: Vec>> = vec![ + /// vec![ + /// Box::new((1, 2, 3, 4)), + /// Box::new((5, 6, 7, 8)), + /// ], + /// vec![ + /// Box::new((9, 10, 11, 12)), + /// ], + /// ]; + /// + /// let shared_point_matrix: Rc>>> = Rc::new(point_matrix); + /// + /// let container = PointMatrixContainer { + /// matrix: shared_point_matrix, + /// }; + /// + /// // ... + /// } + /// ``` + /// Use instead: + /// ### Example + /// ```no_run + /// # use std::rc::Rc; + /// type PointMatrix = Vec>>; + /// type SharedPointMatrix = Rc; + /// + /// struct PointMatrixContainer { + /// matrix: SharedPointMatrix, + /// } + /// + /// fn main() { + /// let point_matrix: PointMatrix = vec![ + /// vec![ + /// Box::new((1, 2, 3, 4)), + /// Box::new((5, 6, 7, 8)), + /// ], + /// vec![ + /// Box::new((9, 10, 11, 12)), + /// ], + /// ]; + /// + /// let shared_point_matrix: SharedPointMatrix = Rc::new(point_matrix); + /// + /// let container = PointMatrixContainer { + /// matrix: shared_point_matrix, + /// }; + /// + /// // ... /// } /// ``` #[clippy::version = "pre 1.29.0"] @@ -335,30 +386,22 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(def_id); - self.check_fn_decl( - cx, - decl, - CheckTyContext { - is_in_trait_impl, - is_exported, - in_body: matches!(fn_kind, FnKind::Closure), - ..CheckTyContext::default() - }, - ); + self.check_fn_decl(cx, decl, CheckTyContext { + is_in_trait_impl, + is_exported, + in_body: matches!(fn_kind, FnKind::Closure), + ..CheckTyContext::default() + }); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { let is_exported = cx.effective_visibilities.is_exported(item.owner_id.def_id); match item.kind { - ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty( - cx, - ty, - CheckTyContext { - is_exported, - ..CheckTyContext::default() - }, - ), + ItemKind::Static(ty, _, _) | ItemKind::Const(ty, _, _) => self.check_ty(cx, ty, CheckTyContext { + is_exported, + ..CheckTyContext::default() + }), // functions, enums, structs, impls and traits are covered _ => (), } @@ -376,14 +419,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { false }; - self.check_ty( - cx, - ty, - CheckTyContext { - is_in_trait_impl, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, ty, CheckTyContext { + is_in_trait_impl, + ..CheckTyContext::default() + }); }, // Methods are covered by check_fn. // Type aliases are ignored because oftentimes it's impossible to @@ -399,14 +438,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { let is_exported = cx.effective_visibilities.is_exported(field.def_id); - self.check_ty( - cx, - field.ty, - CheckTyContext { - is_exported, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, field.ty, CheckTyContext { + is_exported, + ..CheckTyContext::default() + }); } fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &TraitItem<'tcx>) { @@ -434,14 +469,10 @@ impl<'tcx> LateLintPass<'tcx> for Types { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) { if let Some(ty) = local.ty { - self.check_ty( - cx, - ty, - CheckTyContext { - in_body: true, - ..CheckTyContext::default() - }, - ); + self.check_ty(cx, ty, CheckTyContext { + in_body: true, + ..CheckTyContext::default() + }); } } } diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs index 0801eace4fcf..1a656088b174 100644 --- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs +++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs @@ -9,7 +9,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TypeVisitableExt; use rustc_span::symbol::sym; -use super::{utils, REDUNDANT_ALLOCATION}; +use super::{REDUNDANT_ALLOCATION, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool { let mut applicability = Applicability::MaybeIncorrect; diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs index 7fcfd5c8f350..0b64fddb447b 100644 --- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; use rustc_hir::{GenericParamKind, TyKind}; use rustc_lint::LateContext; use rustc_target::spec::abi::Abi; diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs index 29996a6f783e..230239335c65 100644 --- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs +++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs @@ -6,8 +6,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, GenericArg, LangItem, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::LateContext; -use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::TypeVisitableExt; +use rustc_middle::ty::layout::LayoutOf; use rustc_span::symbol::sym; use super::VEC_BOX; diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs index 42100e1d755c..3fc08e8192d4 100644 --- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs +++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs @@ -5,7 +5,7 @@ use rustc_data_structures::fx::FxHashMap; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::{walk_body, walk_expr, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_body, walk_expr}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Item, ItemKind, Node, QPath, TyKind}; use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; @@ -13,8 +13,8 @@ use rustc_middle::hir::map::Map; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, AssocKind, Ty, TyCtxt}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{sym, Span}; +use rustc_span::symbol::{Ident, kw}; +use rustc_span::{Span, sym}; use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; use std::ops::ControlFlow; @@ -257,13 +257,10 @@ fn is_default_method_on_current_ty<'tcx>(tcx: TyCtxt<'tcx>, qpath: QPath<'tcx>, } if matches!( ty.kind, - TyKind::Path(QPath::Resolved( - _, - hir::Path { - res: Res::SelfTyAlias { .. }, - .. - }, - )) + TyKind::Path(QPath::Resolved(_, hir::Path { + res: Res::SelfTyAlias { .. }, + .. + },)) ) { return true; } diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index f51c5f74d993..44eb0a6c593d 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -4,12 +4,12 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_lint_allowed; use clippy_utils::source::walk_span_to_context; -use clippy_utils::visitors::{for_each_expr, Descend}; +use clippy_utils::visitors::{Descend, for_each_expr}; use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::impl_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs index 88039372ebd2..cfa565cf8037 100644 --- a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs +++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs @@ -5,8 +5,8 @@ use rustc_hir_analysis::lower_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::def_id::LocalDefId; use rustc_span::Span; +use rustc_span::def_id::LocalDefId; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs index 9ffcfcc0f50c..93ed15777e01 100644 --- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs +++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty}; -use clippy_utils::{is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while}; use rustc_hir::{Block, Expr, ExprKind, HirId, PatKind, PathSegment, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; // TODO: add `ReadBuf` (RFC 2930) in "How to fix" once it is available in std declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index a8cc2f979633..87478a120dd0 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -5,7 +5,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::{ClauseKind, GenericPredicates, ProjectionPredicate, TraitPredicate}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs index 80b661a757c2..bf2d75ea1a96 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs @@ -4,7 +4,7 @@ use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source, i use core::ops::ControlFlow; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::intravisit::{walk_body, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_body}; use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, LetStmt, MatchSource, Node, PatKind, QPath, TyKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::{in_external_macro, is_from_async_await}; diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 978d49f09c3a..41b2ca5d268c 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanRangeExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind}; use rustc_lint::LateContext; -use super::{utils, UNIT_ARG}; +use super::{UNIT_ARG, utils}; pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if expr.span.from_expansion() { diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs index 080efe983c2a..937e35dea96d 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs @@ -4,15 +4,15 @@ use clippy_utils::source::snippet; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{contains_return, is_res_lang_ctor, path_res, return_ty}; use rustc_errors::Applicability; -use rustc_hir::intravisit::FnKind; use rustc_hir::LangItem::{OptionSome, ResultOk}; +use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, ExprKind, FnDecl, Impl, ItemKind, Node}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::def_id::LocalDefId; use rustc_span::symbol::sym; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 98f0be3135db..104be63bb157 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -1,14 +1,14 @@ #![allow(clippy::wildcard_imports, clippy::enum_glob_use)] -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::over; +use rustc_ast::PatKind::*; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; -use rustc_ast::PatKind::*; -use rustc_ast::{self as ast, Mutability, Pat, PatKind, DUMMY_NODE_ID}; +use rustc_ast::{self as ast, DUMMY_NODE_ID, Mutability, Pat, PatKind}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -16,7 +16,7 @@ use rustc_session::impl_lint_pass; use rustc_span::DUMMY_SP; use std::cell::Cell; use std::mem; -use thin_vec::{thin_vec, ThinVec}; +use thin_vec::{ThinVec, thin_vec}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs index 309eaedac8d2..e70d2a2dafee 100644 --- a/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs +++ b/src/tools/clippy/clippy_lints/src/unsafe_removed_from_name.rs @@ -2,8 +2,8 @@ use clippy_utils::diagnostics::span_lint; use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::symbol::Ident; use rustc_span::Span; +use rustc_span::symbol::Ident; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index 738fba54fa83..a1f08cf6623b 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::is_def_id_trait_method; use rustc_hir::def::DefKind; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{Body, Expr, ExprKind, FnDecl, Node, YieldSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; -use rustc_span::def_id::{LocalDefId, LocalDefIdSet}; use rustc_span::Span; +use rustc_span::def_id::{LocalDefId, LocalDefIdSet}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs index af7abd009d25..d2a21b11ef47 100644 --- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs +++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs @@ -5,7 +5,7 @@ use hir::{ExprKind, HirId, PatKind}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs index 86a811e17ca9..71aa57e0a14c 100644 --- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs +++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable}; use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators}; use rustc_ast::Mutability; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Block, Expr, ExprKind, HirId, LetStmt, Node, PatKind, PathSegment, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs new file mode 100644 index 000000000000..9fd6ebccd02f --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs @@ -0,0 +1,94 @@ +use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::source::snippet_opt; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::{Item, ItemKind, UseKind}; +use rustc_lint::{LateContext, LateLintPass, LintContext as _}; +use rustc_middle::lint::in_external_macro; +use rustc_middle::ty::Visibility; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::kw; + +declare_clippy_lint! { + /// ### What it does + /// Checks for `use Trait` where the Trait is only used for its methods and not referenced by a path directly. + /// + /// ### Why is this bad? + /// Traits imported that aren't used directly can be imported anonymously with `use Trait as _`. + /// It is more explicit, avoids polluting the current scope with unused names and can be useful to show which imports are required for traits. + /// + /// ### Example + /// ```no_run + /// use std::fmt::Write; + /// + /// fn main() { + /// let mut s = String::new(); + /// let _ = write!(s, "hello, world!"); + /// println!("{s}"); + /// } + /// ``` + /// Use instead: + /// ```no_run + /// use std::fmt::Write as _; + /// + /// fn main() { + /// let mut s = String::new(); + /// let _ = write!(s, "hello, world!"); + /// println!("{s}"); + /// } + /// ``` + #[clippy::version = "1.83.0"] + pub UNUSED_TRAIT_NAMES, + restriction, + "use items that import a trait but only use it anonymously" +} + +pub struct UnusedTraitNames { + msrv: Msrv, +} + +impl UnusedTraitNames { + pub fn new(conf: &'static Conf) -> Self { + Self { + msrv: conf.msrv.clone(), + } + } +} + +impl_lint_pass!(UnusedTraitNames => [UNUSED_TRAIT_NAMES]); + +impl<'tcx> LateLintPass<'tcx> for UnusedTraitNames { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if self.msrv.meets(msrvs::UNDERSCORE_IMPORTS) + && !in_external_macro(cx.sess(), item.span) + && let ItemKind::Use(path, UseKind::Single) = item.kind + // Ignore imports that already use Underscore + && item.ident.name != kw::Underscore + // Only check traits + && let Some(Res::Def(DefKind::Trait, _)) = path.res.first() + && cx.tcx.maybe_unused_trait_imports(()).contains(&item.owner_id.def_id) + // Only check this import if it is visible to its module only (no pub, pub(crate), ...) + && let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id) + && cx.tcx.visibility(item.owner_id.def_id) == Visibility::Restricted(module.to_def_id()) + && let Some(last_segment) = path.segments.last() + && let Some(snip) = snippet_opt(cx, last_segment.ident.span) + && !is_from_proc_macro(cx, &last_segment.ident) + { + let complete_span = last_segment.ident.span.to(item.ident.span); + span_lint_and_sugg( + cx, + UNUSED_TRAIT_NAMES, + complete_span, + "importing trait that is only used anonymously", + "use", + format!("{snip} as _"), + Applicability::MachineApplicable, + ); + } + } + + extract_msrv_attr!(LateContext); +} diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index 65f431d338bd..d5309aade7aa 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{position_before_rarrow, SpanRangeExt}; +use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; use rustc_ast::visit::FnKind; -use rustc_ast::{ast, ClosureBinder}; +use rustc_ast::{ClosureBinder, ast}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::declare_lint_pass; diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs index 0b4ea0752b6b..596f0fd9c8b0 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap.rs @@ -3,7 +3,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::is_potentially_local_place; use clippy_utils::{higher, path_to_local}; use rustc_errors::Applicability; -use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId}; use rustc_lint::{LateContext, LateLintPass}; @@ -13,7 +13,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_session::declare_lint_pass; use rustc_span::def_id::LocalDefId; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs index f77badd97b7a..9b9a2ffbbc80 100644 --- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs +++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_hir::ImplItemKind; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs index 5da48f4f7fbd..e340b419bd07 100644 --- a/src/tools/clippy/clippy_lints/src/use_self.rs +++ b/src/tools/clippy/clippy_lints/src/use_self.rs @@ -1,5 +1,5 @@ -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::ty::same_type_and_consts; @@ -7,7 +7,7 @@ use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{walk_inf, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_inf, walk_ty}; use rustc_hir::{ self as hir, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind, HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatKind, Path, QPath, Ty, TyKind, diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index 2f7d54e73ed7..29a7949b3435 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -12,7 +12,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::traits::ObligationCause; use rustc_middle::ty::{self, EarlyBinder, GenericArg, GenericArgsRef, Ty, TypeVisitableExt}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; declare_clippy_lint! { @@ -217,12 +217,12 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(.., args, _) => { cx.typeck_results().type_dependent_def_id(parent.hir_id).map(|did| { - return ( + ( did, args, cx.typeck_results().node_args(parent.hir_id), MethodOrFunction::Method, - ); + ) }) }, _ => None, diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index 0cce45290cfe..31f9d84f5e46 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -1,6 +1,6 @@ use clippy_utils::{get_attr, higher}; -use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_ast::LitIntType; +use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::{ self as hir, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs index f50ce6c99dea..8f314ce7a60c 100644 --- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -3,10 +3,10 @@ use clippy_utils::source::SpanRangeExt; use itertools::Itertools; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::{hygiene, Span}; +use rustc_span::{Span, hygiene}; use std::iter::once; use std::mem; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs index f75296826755..d8f101a8614d 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; -use clippy_utils::{is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt, SpanlessEq}; +use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; 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 0ffcb433481e..d444ad45087c 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 @@ -2,11 +2,11 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::def_path_res; use clippy_utils::diagnostics::span_lint; use rustc_hir as hir; -use rustc_hir::def::DefKind; use rustc_hir::Item; +use rustc_hir::def::DefKind; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::FloatTy; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_session::declare_lint_pass; use rustc_span::symbol::Symbol; diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs index 20526113d69e..7c45a5b2f097 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs @@ -12,7 +12,7 @@ use rustc_middle::hir::nested_filter; use rustc_session::impl_lint_pass; use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use {rustc_ast as ast, rustc_hir as hir}; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs index 41183700f09e..76b0a52621be 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs +++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/unnecessary_def_path.rs @@ -8,12 +8,12 @@ use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::{Allocation, GlobalAlloc}; use rustc_middle::ty::{self, Ty}; use rustc_session::impl_lint_pass; -use rustc_span::symbol::Symbol; use rustc_span::Span; +use rustc_span::symbol::Symbol; use std::str; diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs index d3e49bff422c..ce4f41e854d5 100644 --- a/src/tools/clippy/clippy_lints/src/vec.rs +++ b/src/tools/clippy/clippy_lints/src/vec.rs @@ -1,8 +1,8 @@ use std::collections::BTreeMap; use std::ops::ControlFlow; -use clippy_config::msrvs::{self, Msrv}; use clippy_config::Conf; +use clippy_config::msrvs::{self, Msrv}; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::source::SpanRangeExt; @@ -15,7 +15,7 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; -use rustc_span::{sym, DesugaringKind, Span}; +use rustc_span::{DesugaringKind, Span, sym}; #[expect(clippy::module_name_repetitions)] pub struct UselessVec { diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs index a599415a2dd5..91dff5a95236 100644 --- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::{get_vec_init_kind, VecInitKind}; +use clippy_utils::higher::{VecInitKind, get_vec_init_kind}; use clippy_utils::source::snippet; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{get_parent_expr, path_to_local_id}; diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 7a85196cecaa..2e5fc5834e24 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -5,8 +5,8 @@ use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_session::declare_lint_pass; -use rustc_span::symbol::kw; use rustc_span::Span; +use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs index 7d9e58ad2f8d..405310512dff 100644 --- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs +++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs @@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::symbol::kw; -use rustc_span::{sym, BytePos}; +use rustc_span::{BytePos, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs index ec5a5896fb2b..1dd46ed5a891 100644 --- a/src/tools/clippy/clippy_lints/src/write.rs +++ b/src/tools/clippy/clippy_lints/src/write.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::is_in_test; -use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall}; -use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt}; +use clippy_utils::macros::{FormatArgsStorage, MacroCall, format_arg_removal_span, root_macro_call_first_node}; +use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; use rustc_ast::token::LitKind; use rustc_ast::{ FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder, @@ -12,7 +12,7 @@ use rustc_errors::Applicability; use rustc_hir::{Expr, Impl, Item, ItemKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{sym, BytePos, Span}; +use rustc_span::{BytePos, Span, sym}; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs new file mode 100644 index 000000000000..ba2a80ee66b0 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs @@ -0,0 +1,334 @@ +use ControlFlow::{Break, Continue}; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::{fn_def_id, get_enclosing_block, match_any_def_paths, match_def_path, path_to_local_id, paths}; +use rustc_ast::Mutability; +use rustc_errors::Applicability; +use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_local}; +use rustc_hir::{Expr, ExprKind, HirId, LetStmt, Node, PatKind, Stmt, StmtKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; +use std::ops::ControlFlow; + +declare_clippy_lint! { + /// ### What it does + /// Looks for code that spawns a process but never calls `wait()` on the child. + /// + /// ### Why is this bad? + /// As explained in the [standard library documentation](https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning), + /// calling `wait()` is necessary on Unix platforms to properly release all OS resources associated with the process. + /// Not doing so will effectively leak process IDs and/or other limited global resources, + /// which can eventually lead to resource exhaustion, so it's recommended to call `wait()` in long-running applications. + /// Such processes are called "zombie processes". + /// + /// ### Example + /// ```rust + /// use std::process::Command; + /// + /// let _child = Command::new("ls").spawn().expect("failed to execute child"); + /// ``` + /// Use instead: + /// ```rust + /// use std::process::Command; + /// + /// let mut child = Command::new("ls").spawn().expect("failed to execute child"); + /// child.wait().expect("failed to wait on child"); + /// ``` + #[clippy::version = "1.74.0"] + pub ZOMBIE_PROCESSES, + suspicious, + "not waiting on a spawned child process" +} +declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]); + +impl<'tcx> LateLintPass<'tcx> for ZombieProcesses { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { + if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind + && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def() + && match_def_path(cx, child_adt.did(), &paths::CHILD) + { + match cx.tcx.parent_hir_node(expr.hir_id) { + Node::LetStmt(local) + if let PatKind::Binding(_, local_id, ..) = local.pat.kind + && let Some(enclosing_block) = get_enclosing_block(cx, expr.hir_id) => + { + let mut vis = WaitFinder::WalkUpTo(cx, local_id); + + // If it does have a `wait()` call, we're done. Don't lint. + if let Break(BreakReason::WaitFound) = walk_block(&mut vis, enclosing_block) { + return; + } + + // Don't emit a suggestion since the binding is used later + check(cx, expr, false); + }, + Node::LetStmt(&LetStmt { pat, .. }) if let PatKind::Wild = pat.kind => { + // `let _ = child;`, also dropped immediately without `wait()`ing + check(cx, expr, true); + }, + Node::Stmt(&Stmt { + kind: StmtKind::Semi(_), + .. + }) => { + // Immediately dropped. E.g. `std::process::Command::new("echo").spawn().unwrap();` + check(cx, expr, true); + }, + _ => {}, + } + } + } +} + +enum BreakReason { + WaitFound, + EarlyReturn, +} + +/// A visitor responsible for finding a `wait()` call on a local variable. +/// +/// Conditional `wait()` calls are assumed to not call wait: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// c.wait(); +/// } +/// ``` +/// +/// Note that this visitor does NOT explicitly look for `wait()` calls directly, but rather does the +/// inverse -- checking if all uses of the local are either: +/// - a field access (`child.{stderr,stdin,stdout}`) +/// - calling `id` or `kill` +/// - no use at all (e.g. `let _x = child;`) +/// - taking a shared reference (`&`), `wait()` can't go through that +/// +/// None of these are sufficient to prevent zombie processes. +/// Doing it like this means more FNs, but FNs are better than FPs. +/// +/// `return` expressions, conditional or not, short-circuit the visitor because +/// if a `wait()` call hadn't been found at that point, it might never reach one at a later point: +/// ```ignore +/// let mut c = Command::new("").spawn().unwrap(); +/// if true { +/// return; // Break(BreakReason::EarlyReturn) +/// } +/// c.wait(); // this might not be reachable +/// ``` +enum WaitFinder<'a, 'tcx> { + WalkUpTo(&'a LateContext<'tcx>, HirId), + Found(&'a LateContext<'tcx>, HirId), +} + +impl<'a, 'tcx> Visitor<'tcx> for WaitFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_local(&mut self, l: &'tcx LetStmt<'tcx>) -> Self::Result { + if let Self::WalkUpTo(cx, local_id) = *self + && let PatKind::Binding(_, pat_id, ..) = l.pat.kind + && local_id == pat_id + { + *self = Self::Found(cx, local_id); + } + + walk_local(self, l) + } + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) -> Self::Result { + let Self::Found(cx, local_id) = *self else { + return walk_expr(self, ex); + }; + + if path_to_local_id(ex, local_id) { + match cx.tcx.parent_hir_node(ex.hir_id) { + Node::Stmt(Stmt { + kind: StmtKind::Semi(_), + .. + }) => {}, + Node::Expr(expr) if let ExprKind::Field(..) = expr.kind => {}, + Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {}, + Node::Expr(expr) + if let Some(fn_did) = fn_def_id(cx, expr) + && match_any_def_paths(cx, fn_did, &[&paths::CHILD_ID, &paths::CHILD_KILL]).is_some() => {}, + + // Conservatively assume that all other kinds of nodes call `.wait()` somehow. + _ => return Break(BreakReason::WaitFound), + } + } else { + match ex.kind { + ExprKind::Ret(..) => return Break(BreakReason::EarlyReturn), + ExprKind::If(cond, then, None) => { + walk_expr(self, cond)?; + + // A `wait()` call in an if expression with no else is not enough: + // + // let c = spawn(); + // if true { + // c.wait(); + // } + // + // This might not call wait(). However, early returns are propagated, + // because they might lead to a later wait() not being called. + if let Break(BreakReason::EarlyReturn) = walk_expr(self, then) { + return Break(BreakReason::EarlyReturn); + } + + return Continue(()); + }, + + ExprKind::If(cond, then, Some(else_)) => { + walk_expr(self, cond)?; + + #[expect(clippy::unnested_or_patterns)] + match (walk_expr(self, then), walk_expr(self, else_)) { + (Continue(()), Continue(())) + + // `wait()` in one branch but nothing in the other does not count + | (Continue(()), Break(BreakReason::WaitFound)) + | (Break(BreakReason::WaitFound), Continue(())) => {}, + + // `wait()` in both branches is ok + (Break(BreakReason::WaitFound), Break(BreakReason::WaitFound)) => { + return Break(BreakReason::WaitFound); + }, + + // Propagate early returns in either branch + (Break(BreakReason::EarlyReturn), _) | (_, Break(BreakReason::EarlyReturn)) => { + return Break(BreakReason::EarlyReturn); + }, + } + + return Continue(()); + }, + _ => {}, + } + } + + walk_expr(self, ex) + } +} + +/// This function has shared logic between the different kinds of nodes that can trigger the lint. +/// +/// In particular, `let = ;` requires some custom additional logic +/// such as checking that the binding is not used in certain ways, which isn't necessary for +/// `let _ = ;`. +/// +/// This checks if the program doesn't unconditionally exit after the spawn expression. +fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, emit_suggestion: bool) { + let Some(block) = get_enclosing_block(cx, spawn_expr.hir_id) else { + return; + }; + + let mut vis = ExitPointFinder { + cx, + state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + }; + if let Break(ExitCallFound) = vis.visit_block(block) { + // Visitor found an unconditional `exit()` call, so don't lint. + return; + } + + span_lint_and_then( + cx, + ZOMBIE_PROCESSES, + spawn_expr.span, + "spawned process is never `wait()`ed on", + |diag| { + if emit_suggestion { + diag.span_suggestion( + spawn_expr.span.shrink_to_hi(), + "try", + ".wait()", + Applicability::MaybeIncorrect, + ); + } else { + diag.note("consider calling `.wait()`"); + } + + diag.note("not doing so might leave behind zombie processes") + .note("see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning"); + }, + ); +} + +/// Checks if the given expression exits the process. +fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + fn_def_id(cx, expr).is_some_and(|fn_did| { + cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || match_def_path(cx, fn_did, &paths::ABORT) + }) +} + +#[derive(Debug)] +enum ExitPointState { + /// Still walking up to the expression that initiated the visitor. + WalkUpTo(HirId), + /// We're inside of a control flow construct (e.g. `if`, `match`, `loop`) + /// Within this, we shouldn't accept any `exit()` calls in here, but we can leave all of these + /// constructs later and still continue looking for an `exit()` call afterwards. Example: + /// ```ignore + /// Command::new("").spawn().unwrap(); + /// + /// if true { // depth=1 + /// if true { // depth=2 + /// match () { // depth=3 + /// () => loop { // depth=4 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ conditional exit call, ignored + /// + /// } // depth=3 + /// } // depth=2 + /// } // depth=1 + /// } // depth=0 + /// + /// std::process::exit(); + /// ^^^^^^^^^^^^^^^^^^^^^ this exit call is accepted because we're now unconditionally calling it + /// ``` + /// We can only get into this state from `NoExit`. + InControlFlow { depth: u32 }, + /// No exit call found yet, but looking for one. + NoExit, +} + +fn expr_enters_control_flow(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Loop(..)) +} + +struct ExitPointFinder<'a, 'tcx> { + state: ExitPointState, + cx: &'a LateContext<'tcx>, +} + +struct ExitCallFound; + +impl<'a, 'tcx> Visitor<'tcx> for ExitPointFinder<'a, 'tcx> { + type Result = ControlFlow; + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) -> Self::Result { + match self.state { + ExitPointState::WalkUpTo(id) if expr.hir_id == id => { + self.state = ExitPointState::NoExit; + walk_expr(self, expr) + }, + ExitPointState::NoExit if expr_enters_control_flow(expr) => { + self.state = ExitPointState::InControlFlow { depth: 1 }; + walk_expr(self, expr)?; + if let ExitPointState::InControlFlow { .. } = self.state { + self.state = ExitPointState::NoExit; + } + Continue(()) + }, + ExitPointState::NoExit if is_exit_expression(self.cx, expr) => Break(ExitCallFound), + ExitPointState::InControlFlow { ref mut depth } if expr_enters_control_flow(expr) => { + *depth += 1; + walk_expr(self, expr)?; + match self.state { + ExitPointState::InControlFlow { depth: 1 } => self.state = ExitPointState::NoExit, + ExitPointState::InControlFlow { ref mut depth } => *depth -= 1, + _ => {}, + } + Continue(()) + }, + _ => Continue(()), + } + } +} diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml index 629ce9d04d43..fe30b10c435e 100644 --- a/src/tools/clippy/clippy_utils/Cargo.toml +++ b/src/tools/clippy/clippy_utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clippy_utils" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs index de8bbb619f8e..49323492e124 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs @@ -242,13 +242,16 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool { } fn eq_coroutine_kind(a: Option, b: Option) -> bool { - match (a, b) { + matches!( + (a, b), (Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. })) - | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) - | (Some(CoroutineKind::AsyncGen { .. }), Some(CoroutineKind::AsyncGen { .. })) - | (None, None) => true, - _ => false, - } + | (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) + | ( + Some(CoroutineKind::AsyncGen { .. }), + Some(CoroutineKind::AsyncGen { .. }) + ) + | (None, None) + ) } pub fn eq_field(l: &ExprField, r: &ExprField) -> bool { diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs index eefcbabd835d..032cd3ed7399 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/ident_iter.rs @@ -1,5 +1,5 @@ use core::iter::FusedIterator; -use rustc_ast::visit::{walk_attribute, walk_expr, Visitor}; +use rustc_ast::visit::{Visitor, walk_attribute, walk_expr}; use rustc_ast::{Attribute, Expr}; use rustc_span::symbol::Ident; diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 42c8b218d14e..edc9c6ccdff8 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -4,7 +4,7 @@ use rustc_lexer::TokenKind; use rustc_lint::LateContext; use rustc_middle::ty::{AdtDef, TyCtxt}; use rustc_session::Session; -use rustc_span::{sym, Span}; +use rustc_span::{Span, sym}; use std::str::FromStr; use crate::source::SpanRangeExt; @@ -183,15 +183,15 @@ pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] - while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) { - let mut iter = iter.by_ref().skip_while(|(t, _)| { + while iter.any(|(t, ..)| matches!(t, TokenKind::Pound)) { + let mut iter = iter.by_ref().skip_while(|(t, ..)| { matches!( t, TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } ) }); - if matches!(iter.next(), Some((TokenKind::OpenBracket, _))) - && matches!(iter.next(), Some((TokenKind::Ident, "cfg"))) + if matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) { return true; } diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 2bd6837d973b..9143d292f670 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -12,9 +12,9 @@ //! code was written, and check if the span contains that text. Note this will only work correctly //! if the span is not from a `macro_rules` based macro. +use rustc_ast::AttrStyle; use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy}; use rustc_ast::token::CommentKind; -use rustc_ast::AttrStyle; use rustc_hir::intravisit::FnKind; use rustc_hir::{ Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl, @@ -24,7 +24,7 @@ use rustc_hir::{ use rustc_lint::{LateContext, LintContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::symbol::{kw, Ident}; +use rustc_span::symbol::{Ident, kw}; use rustc_span::{Span, Symbol}; use rustc_target::spec::abi::Abi; diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 760d5bc95f7f..bf47cf6d372d 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -1,24 +1,24 @@ #![allow(clippy::float_cmp)] use crate::macros::HirNode; -use crate::source::{walk_span_to_context, SpanRangeExt}; +use crate::source::{SpanRangeExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, unsext}; -use rustc_apfloat::ieee::{Half, Quad}; use rustc_apfloat::Float; +use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_data_structures::sync::Lrc; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp}; use rustc_lexer::tokenize; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::{alloc_range, Scalar}; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::{Scalar, alloc_range}; use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy}; use rustc_middle::{bug, mir, span_bug}; use rustc_span::def_id::DefId; use rustc_span::symbol::Ident; -use rustc_span::{sym, SyntaxContext}; +use rustc_span::{SyntaxContext, sym}; use rustc_target::abi::Size; use std::cell::Cell; use std::cmp::Ordering; @@ -581,7 +581,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option> { - use self::Constant::{Int, F32, F64}; + use self::Constant::{F32, F64, Int}; match *o { Int(value) => { let ty::Int(ity) = *ty.kind() else { return None }; diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index a6dd12a28c5b..9420d84b9591 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -14,12 +14,12 @@ use crate::ty::{all_predicates_of, is_copy}; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_span::{sym, Symbol}; +use rustc_span::{Symbol, sym}; use std::{cmp, ops}; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs index e09cc9edf3ac..3175a9a1dd31 100644 --- a/src/tools/clippy/clippy_utils/src/higher.rs +++ b/src/tools/clippy/clippy_utils/src/higher.rs @@ -3,14 +3,14 @@ #![deny(clippy::missing_docs_in_private_items)] use crate::consts::{ConstEvalCtxt, Constant}; -use crate::ty::is_type_diagnostic_item; use crate::is_expn_of; +use crate::ty::is_type_diagnostic_item; use rustc_ast::ast; use rustc_hir as hir; use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath}; use rustc_lint::LateContext; -use rustc_span::{sym, symbol, Span}; +use rustc_span::{Span, sym, symbol}; /// The essential nodes of a desugared for loop as well as the entire span: /// `for pat in arg { body }` becomes `(pat, arg, body)`. Returns `(pat, arg, body, span)`. diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index f61ef9ac1b05..76900379ac78 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,20 +1,20 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt}; +use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; use crate::tokenize_with_text; use rustc_ast::ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxHasher; -use rustc_hir::def::Res; use rustc_hir::MatchSource::TryDesugar; +use rustc_hir::def::Res; use rustc_hir::{ ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, }; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::LateContext; use rustc_middle::ty::TypeckResults; -use rustc_span::{sym, BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext}; +use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym}; use std::hash::{Hash, Hasher}; use std::ops::Range; @@ -1194,8 +1194,8 @@ fn eq_span_tokens( && let Some(rsrc) = right.get_source_range(cx) && let Some(rsrc) = rsrc.as_str() { - let pred = |t: &(_, _)| pred(t.0); - let map = |(_, x)| x; + let pred = |&(token, ..): &(TokenKind, _, _)| pred(token); + let map = |(_, source, _)| source; let ltok = tokenize_with_text(lsrc).filter(pred).map(map); let rtok = tokenize_with_text(rsrc).filter(pred).map(map); diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index aed0bc565d8e..6dbc3334157e 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -78,7 +78,7 @@ pub mod visitors; pub use self::attrs::*; pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match}; pub use self::hir_utils::{ - both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash, + HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, }; use core::mem; @@ -94,20 +94,20 @@ use rustc_ast::ast::{self, LitKind, RangeLimits}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; +use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalModDefId, LOCAL_CRATE}; +use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; -use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; -use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; +use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, - ConstContext, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, - ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, - Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, - TraitRef, TyKind, UnOp, + self as hir, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, + Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, + ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, + PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, + TyKind, UnOp, def, }; -use rustc_lexer::{tokenize, TokenKind}; +use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::Const; @@ -120,12 +120,12 @@ use rustc_middle::ty::{ }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; -use rustc_span::symbol::{kw, Ident, Symbol}; -use rustc_span::{sym, Span}; +use rustc_span::symbol::{Ident, Symbol, kw}; +use rustc_span::{InnerSpan, Span, sym}; use rustc_target::abi::Integer; use visitors::Visitable; -use crate::consts::{mir_to_const, ConstEvalCtxt, Constant}; +use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -263,9 +263,13 @@ pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> } } - /// Checks if `{ctor_call_id}(...)` is `{enum_item}::{variant_name}(...)`. -pub fn is_enum_variant_ctor(cx: &LateContext<'_>, enum_item: Symbol, variant_name: Symbol, ctor_call_id: DefId) -> bool { +pub fn is_enum_variant_ctor( + cx: &LateContext<'_>, + enum_item: Symbol, + variant_name: Symbol, + ctor_call_id: DefId, +) -> bool { let Some(enum_def_id) = cx.tcx.get_diagnostic_item(enum_item) else { return false; }; @@ -664,6 +668,17 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, name: Symbol) -> Vec { + tcx.crates(()) + .iter() + .copied() + .filter(move |&num| tcx.crate_name(num) == name) + .map(CrateNum::as_def_id) + .map(|id| Res::Def(tcx.def_kind(id), id)) + .collect() +} + /// Resolves a def path like `std::vec::Vec`. /// /// Can return multiple resolutions when there are multiple versions of the same crate, e.g. @@ -674,15 +689,7 @@ fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec, path: &[&str]) -> Vec { - fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator + '_ { - tcx.crates(()) - .iter() - .copied() - .filter(move |&num| tcx.crate_name(num) == name) - .map(CrateNum::as_def_id) - } - - let (base, mut path) = match *path { + let (base, path) = match *path { [primitive] => { return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)]; }, @@ -698,18 +705,25 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { None }; - let starts = find_primitive_impls(tcx, base) - .chain(find_crates(tcx, base_sym)) + let crates = find_primitive_impls(tcx, base) .chain(local_crate) - .map(|id| Res::Def(tcx.def_kind(id), id)); + .map(|id| Res::Def(tcx.def_kind(id), id)) + .chain(find_crates(tcx, base_sym)) + .collect(); - let mut resolutions: Vec = starts.collect(); + def_path_res_with_base(tcx, crates, path) +} +/// Resolves a def path like `vec::Vec` with the base `std`. +/// +/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up +/// items from the same crate repeatedly, although should still be used sparingly. +pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec, mut path: &[&str]) -> Vec { while let [segment, rest @ ..] = path { path = rest; let segment = Symbol::intern(segment); - resolutions = resolutions + base = base .into_iter() .filter_map(|res| res.opt_def_id()) .flat_map(|def_id| { @@ -727,7 +741,7 @@ pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec { .collect(); } - resolutions + base } /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`]. @@ -2940,13 +2954,14 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU } /// Tokenizes the input while keeping the text associated with each token. -pub fn tokenize_with_text(s: &str) -> impl Iterator { +pub fn tokenize_with_text(s: &str) -> impl Iterator { let mut pos = 0; tokenize(s).map(move |t| { let end = pos + t.len; let range = pos as usize..end as usize; + let inner = InnerSpan::new(range.start, range.end); pos = end; - (t.kind, s.get(range).unwrap_or_default()) + (t.kind, s.get(range).unwrap_or_default(), inner) }) } @@ -2970,8 +2985,8 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool { pub fn span_extract_comment(sm: &SourceMap, span: Span) -> String { let snippet = sm.span_to_snippet(span).unwrap_or_default(); let res = tokenize_with_text(&snippet) - .filter(|(t, _)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) - .map(|(_, s)| s) + .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) + .map(|(_, s, _)| s) .join("\n"); res } @@ -2991,7 +3006,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// pat: Some(a) /// else_body: return None /// ``` - +/// /// And for this example: /// ```ignore /// let Some(FooBar { a, b }) = ex else { return None }; @@ -3001,7 +3016,7 @@ pub fn span_find_starting_semi(sm: &SourceMap, span: Span) -> Span { /// pat: Some(FooBar { a, b }) /// else_body: return None /// ``` - +/// /// We output `Some(a)` in the first instance, and `Some(FooBar { a, b })` in the second, because /// the question mark operator is applicable here. Callers have to check whether we are in a /// constant or not. diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs index 1d7479bff82d..9c4d19ac1f1d 100644 --- a/src/tools/clippy/clippy_utils/src/macros.rs +++ b/src/tools/clippy/clippy_utils/src/macros.rs @@ -1,6 +1,6 @@ #![allow(clippy::similar_names)] // `expr` and `expn` -use crate::visitors::{for_each_expr_without_closures, Descend}; +use crate::visitors::{Descend, for_each_expr_without_closures}; use arrayvec::ArrayVec; use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder}; @@ -10,7 +10,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath}; use rustc_lint::LateContext; use rustc_span::def_id::DefId; use rustc_span::hygiene::{self, MacroKind, SyntaxContext}; -use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol}; +use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym}; use std::ops::ControlFlow; const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[ diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs index 654fb564848e..59bb5e35cda6 100644 --- a/src/tools/clippy/clippy_utils/src/mir/mod.rs +++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs @@ -2,7 +2,7 @@ use rustc_hir::{Expr, HirId}; use rustc_index::bit_set::BitSet; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{ - traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK, + BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal, }; use rustc_middle::ty::TyCtxt; @@ -112,14 +112,10 @@ pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool { /// Convenience wrapper around `visit_local_usage`. pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option { - visit_local_usage( - &[local], - mir, - Location { - block: START_BLOCK, - statement_index: 0, - }, - ) + visit_local_usage(&[local], mir, Location { + block: START_BLOCK, + statement_index: 0, + }) .map(|mut vec| { let LocalUsage { local_use_locs, .. } = vec.remove(0); let mut locations = local_use_locs diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs index a30edc48fff2..11a98b02f337 100644 --- a/src/tools/clippy/clippy_utils/src/paths.rs +++ b/src/tools/clippy/clippy_utils/src/paths.rs @@ -29,7 +29,11 @@ pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; // Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items. -// ... none currently! +pub const ABORT: [&str; 3] = ["std", "process", "abort"]; +pub const CHILD: [&str; 3] = ["std", "process", "Child"]; +pub const CHILD_ID: [&str; 4] = ["std", "process", "Child", "id"]; +pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"]; +pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"]; // Paths in clippy itself pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"]; diff --git a/src/tools/clippy/clippy_utils/src/ptr.rs b/src/tools/clippy/clippy_utils/src/ptr.rs index 991ea428dc33..273c1b0defab 100644 --- a/src/tools/clippy/clippy_utils/src/ptr.rs +++ b/src/tools/clippy/clippy_utils/src/ptr.rs @@ -1,5 +1,5 @@ use crate::source::snippet; -use crate::visitors::{for_each_expr_without_closures, Descend}; +use crate::visitors::{Descend, for_each_expr_without_closures}; use crate::{path_to_local_id, strip_pat_refs}; use core::ops::ControlFlow; use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind}; 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 ce1895b7fb19..7a6107cfe539 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 @@ -18,8 +18,8 @@ use rustc_middle::mir::{ use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause}; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt}; -use rustc_span::symbol::sym; use rustc_span::Span; +use rustc_span::symbol::sym; use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext}; use std::borrow::Cow; diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 482e1e0147b1..4ad7575e7201 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -9,10 +9,10 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource}; use rustc_lint::{EarlyContext, LateContext}; use rustc_middle::ty::TyCtxt; use rustc_session::Session; -use rustc_span::source_map::{original_sp, SourceMap}; +use rustc_span::source_map::{SourceMap, original_sp}; use rustc_span::{ - hygiene, BytePos, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, - DUMMY_SP, + BytePos, DUMMY_SP, FileNameDisplayPreference, Pos, SourceFile, SourceFileAndLine, Span, SpanData, SyntaxContext, + hygiene, }; use std::borrow::Cow; use std::fmt; @@ -666,39 +666,6 @@ pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option { (outer_span.ctxt() == outer).then_some(outer_span) } -/// Removes block comments from the given `Vec` of lines. -/// -/// # Examples -/// -/// ```rust,ignore -/// without_block_comments(vec!["/*", "foo", "*/"]); -/// // => vec![] -/// -/// without_block_comments(vec!["bar", "/*", "foo", "*/"]); -/// // => vec!["bar"] -/// ``` -pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> { - let mut without = vec![]; - - let mut nest_level = 0; - - for line in lines { - if line.contains("/*") { - nest_level += 1; - continue; - } else if line.contains("*/") { - nest_level -= 1; - continue; - } - - if nest_level == 0 { - without.push(line); - } - } - - without -} - /// Trims the whitespace from the start and the end of the span. pub fn trim_span(sm: &SourceMap, span: Span) -> Span { let data = span.data(); @@ -758,15 +725,12 @@ pub fn str_literal_to_char_literal( &snip[1..(snip.len() - 1)] }; - let hint = format!( - "'{}'", - match ch { - "'" => "\\'", - r"\" => "\\\\", - "\\\"" => "\"", // no need to escape `"` in `'"'` - _ => ch, - } - ); + let hint = format!("'{}'", match ch { + "'" => "\\'", + r"\" => "\\\\", + "\\\"" => "\"", // no need to escape `"` in `'"'` + _ => ch, + }); Some(hint) } else { @@ -776,7 +740,7 @@ pub fn str_literal_to_char_literal( #[cfg(test)] mod test { - use super::{reindent_multiline, without_block_comments}; + use super::reindent_multiline; #[test] fn test_reindent_multiline_single_line() { @@ -844,29 +808,4 @@ mod test { z }".into(), true, Some(8))); } - - #[test] - fn test_without_block_comments_lines_without_block_comments() { - let result = without_block_comments(vec!["/*", "", "*/"]); - println!("result: {result:?}"); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]); - assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]); - - let result = without_block_comments(vec!["/* rust", "", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* one-line comment */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]); - assert!(result.is_empty()); - - let result = without_block_comments(vec!["foo", "bar", "baz"]); - assert_eq!(result, vec!["foo", "bar", "baz"]); - } } diff --git a/src/tools/clippy/clippy_utils/src/str_utils.rs b/src/tools/clippy/clippy_utils/src/str_utils.rs index 421b25a77fe8..1588ee452dae 100644 --- a/src/tools/clippy/clippy_utils/src/str_utils.rs +++ b/src/tools/clippy/clippy_utils/src/str_utils.rs @@ -370,9 +370,11 @@ mod test { assert_eq!(camel_case_split("AbcDef"), vec!["Abc", "Def"]); assert_eq!(camel_case_split("Abc"), vec!["Abc"]); assert_eq!(camel_case_split("abcDef"), vec!["abc", "Def"]); - assert_eq!( - camel_case_split("\u{f6}\u{f6}AabABcd"), - vec!["\u{f6}\u{f6}", "Aab", "A", "Bcd"] - ); + assert_eq!(camel_case_split("\u{f6}\u{f6}AabABcd"), vec![ + "\u{f6}\u{f6}", + "Aab", + "A", + "Bcd" + ]); } } diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 0f86d89c980d..3255c51d009c 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -180,8 +180,10 @@ impl<'a> Sugg<'a> { ) -> Self { use rustc_ast::ast::RangeLimits; + let mut snippet = |span: Span| snippet_with_context(cx, span, ctxt, default, app).0; + match expr.kind { - _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::AddrOf(..) | ast::ExprKind::Closure { .. } | ast::ExprKind::If(..) @@ -224,46 +226,38 @@ impl<'a> Sugg<'a> { | ast::ExprKind::While(..) | ast::ExprKind::Await(..) | ast::ExprKind::Err(_) - | ast::ExprKind::Dummy => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0), + | ast::ExprKind::Dummy => Sugg::NonParen(snippet(expr.span)), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp( AssocOp::DotDot, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp( AssocOp::DotDotEq, - lhs.as_ref().map_or("".into(), |lhs| { - snippet_with_context(cx, lhs.span, ctxt, default, app).0 - }), - rhs.as_ref().map_or("".into(), |rhs| { - snippet_with_context(cx, rhs.span, ctxt, default, app).0 - }), + lhs.as_ref().map_or("".into(), |lhs| snippet(lhs.span)), + rhs.as_ref().map_or("".into(), |rhs| snippet(rhs.span)), ), ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp( AssocOp::Assign, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp( astbinop2assignop(op), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( AssocOp::from_ast_binop(op.node), - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, rhs.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(rhs.span), ), ast::ExprKind::Cast(ref lhs, ref ty) | //FIXME(chenyukang), remove this after type ascription is removed from AST ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp( AssocOp::As, - snippet_with_context(cx, lhs.span, ctxt, default, app).0, - snippet_with_context(cx, ty.span, ctxt, default, app).0, + snippet(lhs.span), + snippet(ty.span), ), } } diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs index 22aaaa1b7341..4e3a61a1aa5f 100644 --- a/src/tools/clippy/clippy_utils/src/ty.rs +++ b/src/tools/clippy/clippy_utils/src/ty.rs @@ -12,8 +12,8 @@ use rustc_hir::def_id::DefId; use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; -use rustc_middle::mir::interpret::Scalar; use rustc_middle::mir::ConstValue; +use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ @@ -22,7 +22,7 @@ use rustc_middle::ty::{ TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, }; use rustc_span::symbol::Ident; -use rustc_span::{sym, Span, Symbol, DUMMY_SP}; +use rustc_span::{DUMMY_SP, Span, Symbol, sym}; use rustc_target::abi::VariantIdx; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt; @@ -606,10 +606,13 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), // Unions are always fine right now. // This includes MaybeUninit, the main way people use uninitialized memory. - // For ADTs, we could look at all fields just like for tuples, but that's potentially - // exponential, so let's avoid doing that for now. Code doing that is sketchy enough to - // just use an `#[allow()]`. - ty::Adt(adt, _) => adt.is_union(), + ty::Adt(adt, _) if adt.is_union() => true, + // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit + // can themselves be uninit too. + // This purposefully ignores enums as they may have a discriminant that can't be uninit. + ty::Adt(adt, args) if adt.is_struct() => adt + .all_fields() + .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args))), // For the rest, conservatively assume that they cannot be uninit. _ => false, } diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs index 875ddec259ee..e612e9c6cb60 100644 --- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs @@ -14,14 +14,14 @@ use crate::def_path_res; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_qpath, walk_ty, Visitor}; +use rustc_hir::intravisit::{Visitor, walk_qpath, walk_ty}; use rustc_hir::{self as hir, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty}; use rustc_span::{Span, Symbol}; mod certainty; -use certainty::{join, meet, Certainty, Meet}; +use certainty::{Certainty, Meet, join, meet}; pub fn expr_type_is_certain(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { expr_type_certainty(cx, expr).is_certain() diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index fbf3d95365ac..1230b60c3a6b 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -1,4 +1,4 @@ -use crate::visitors::{for_each_expr, for_each_expr_without_closures, Descend, Visitable}; +use crate::visitors::{Descend, Visitable, for_each_expr, for_each_expr_without_closures}; use crate::{self as utils, get_enclosing_loop_or_multi_call_closure}; use core::ops::ControlFlow; use hir::def::Res; diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index e5b6d3965e93..6d9a85a1181c 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,10 +1,10 @@ use crate::ty::needs_ordered_drop; use crate::{get_enclosing_block, path_to_local_id}; use core::ops::ControlFlow; -use rustc_ast::visit::{try_visit, VisitorResult}; +use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor}; +use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath, Safety, Stmt, UnOp, UnsafeSource, diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml index 31270241b0b2..67a1f7cc72c5 100644 --- a/src/tools/clippy/declare_clippy_lint/Cargo.toml +++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "declare_clippy_lint" -version = "0.1.82" +version = "0.1.83" edition = "2021" publish = false diff --git a/src/tools/clippy/declare_clippy_lint/src/lib.rs b/src/tools/clippy/declare_clippy_lint/src/lib.rs index 6aa24329b065..fefc1a0a6c40 100644 --- a/src/tools/clippy/declare_clippy_lint/src/lib.rs +++ b/src/tools/clippy/declare_clippy_lint/src/lib.rs @@ -5,7 +5,7 @@ use proc_macro::TokenStream; use quote::{format_ident, quote}; use syn::parse::{Parse, ParseStream}; -use syn::{parse_macro_input, Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token}; +use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input}; fn parse_attr(path: [&'static str; LEN], attr: &Attribute) -> Option { if let Meta::NameValue(name_value) = &attr.meta { @@ -140,15 +140,12 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream { let mut category = category.to_string(); - let level = format_ident!( - "{}", - match category.as_str() { - "correctness" => "Deny", - "style" | "suspicious" | "complexity" | "perf" => "Warn", - "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", - _ => panic!("unknown category {category}"), - }, - ); + let level = format_ident!("{}", match category.as_str() { + "correctness" => "Deny", + "style" | "suspicious" | "complexity" | "perf" => "Warn", + "pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow", + _ => panic!("unknown category {category}"), + },); let info_name = format_ident!("{name}_INFO"); diff --git a/src/tools/clippy/lintcheck/src/driver.rs b/src/tools/clippy/lintcheck/src/driver.rs index 2fda2b00f873..87ab14ba0cf8 100644 --- a/src/tools/clippy/lintcheck/src/driver.rs +++ b/src/tools/clippy/lintcheck/src/driver.rs @@ -1,4 +1,4 @@ -use crate::recursive::{deserialize_line, serialize_line, DriverInfo}; +use crate::recursive::{DriverInfo, deserialize_line, serialize_line}; use std::io::{self, BufReader, Write}; use std::net::TcpStream; diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs index 6a662970ae84..57073f523648 100644 --- a/src/tools/clippy/lintcheck/src/recursive.rs +++ b/src/tools/clippy/lintcheck/src/recursive.rs @@ -3,8 +3,8 @@ //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running //! clippy on the crate to the server -use crate::input::RecursiveOptions; use crate::ClippyWarning; +use crate::input::RecursiveOptions; use std::collections::HashSet; use std::io::{BufRead, BufReader, Read, Write}; diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain index 0be2e81810eb..b431599c224e 100644 --- a/src/tools/clippy/rust-toolchain +++ b/src/tools/clippy/rust-toolchain @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2024-08-23" +channel = "nightly-2024-09-22" components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs index 0ac3f35b4465..324f754e6155 100644 --- a/src/tools/clippy/src/driver.rs +++ b/src/tools/clippy/src/driver.rs @@ -15,9 +15,9 @@ extern crate rustc_session; extern crate rustc_span; use rustc_interface::interface; +use rustc_session::EarlyDiagCtxt; use rustc_session::config::ErrorOutputType; use rustc_session::parse::ParseSess; -use rustc_session::EarlyDiagCtxt; use rustc_span::symbol::Symbol; use std::env; @@ -268,8 +268,6 @@ pub fn main() { }, _ => Some(s.to_string()), }) - // FIXME: remove this line in 1.79 to only keep `--cfg clippy`. - .chain(vec!["--cfg".into(), r#"feature="cargo-clippy""#.into()]) .chain(vec!["--cfg".into(), "clippy".into()]) .collect::>(); diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs index 9754254cdd0d..af2aa5192577 100644 --- a/src/tools/clippy/tests/compile-test.rs +++ b/src/tools/clippy/tests/compile-test.rs @@ -2,26 +2,25 @@ #![warn(rust_2018_idioms, unused_lifetimes)] #![allow(unused_extern_crates)] -use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use cargo_metadata::Message; +use cargo_metadata::diagnostic::{Applicability, Diagnostic}; use clippy_config::ClippyConfiguration; +use clippy_lints::LintInfo; use clippy_lints::declared_lints::LINTS; use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED}; -use clippy_lints::LintInfo; use serde::{Deserialize, Serialize}; use test_utils::IS_RUSTC_TEST_SUITE; -use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::custom_flags::Flag; +use ui_test::custom_flags::rustfix::RustfixMode; use ui_test::spanned::Spanned; -use ui_test::test_result::TestRun; -use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling}; +use ui_test::{Args, CommandBuilder, Config, Match, OutputConflictHandling, status_emitter}; use std::collections::{BTreeMap, HashMap}; use std::env::{self, set_var, var_os}; use std::ffi::{OsStr, OsString}; use std::fmt::Write; use std::path::{Path, PathBuf}; -use std::sync::mpsc::{channel, Sender}; +use std::sync::mpsc::{Sender, channel}; use std::{fs, iter, thread}; // Test dependencies may need an `extern crate` here to ensure that they show up @@ -469,15 +468,14 @@ fn applicability_ord(applicability: &Applicability) -> u8 { impl Flag for DiagnosticCollector { fn post_test_action( &self, - _config: &ui_test::per_test_config::TestConfig<'_>, - _cmd: &mut std::process::Command, + _config: &ui_test::per_test_config::TestConfig, output: &std::process::Output, - _build_manager: &ui_test::build_manager::BuildManager<'_>, - ) -> Result, ui_test::Errored> { + _build_manager: &ui_test::build_manager::BuildManager, + ) -> Result<(), ui_test::Errored> { if !output.stderr.is_empty() { self.sender.send(output.stderr.clone()).unwrap(); } - Ok(Vec::new()) + Ok(()) } fn clone_inner(&self) -> Box { diff --git a/src/tools/clippy/tests/config-metadata.rs b/src/tools/clippy/tests/config-metadata.rs index 3e3711873baa..628dfc8f758a 100644 --- a/src/tools/clippy/tests/config-metadata.rs +++ b/src/tools/clippy/tests/config-metadata.rs @@ -1,6 +1,6 @@ #![feature(rustc_private)] -use clippy_config::{get_configuration_metadata, ClippyConfiguration}; +use clippy_config::{ClippyConfiguration, get_configuration_metadata}; use itertools::Itertools; use regex::Regex; use std::borrow::Cow; diff --git a/src/tools/clippy/tests/dogfood.rs b/src/tools/clippy/tests/dogfood.rs index 8d10d5e7161a..858be389a9e6 100644 --- a/src/tools/clippy/tests/dogfood.rs +++ b/src/tools/clippy/tests/dogfood.rs @@ -6,11 +6,9 @@ #![warn(rust_2018_idioms, unused_lifetimes)] use itertools::Itertools; -use std::fs::File; use std::io::{self, IsTerminal}; use std::path::PathBuf; use std::process::Command; -use std::time::SystemTime; use test_utils::IS_RUSTC_TEST_SUITE; use ui_test::Args; @@ -28,11 +26,7 @@ fn main() { println!("dogfood: test"); } } else if !args.skip.iter().any(|arg| arg == "dogfood") { - if args.filters.iter().any(|arg| arg == "collect_metadata") { - collect_metadata(); - } else { - dogfood(); - } + dogfood(); } } @@ -61,47 +55,6 @@ fn dogfood() { ); } -fn collect_metadata() { - assert!(cfg!(feature = "internal")); - - // Setup for validation - let metadata_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("util/gh-pages/lints.json"); - let start_time = SystemTime::now(); - - // Run collection as is - std::env::set_var("ENABLE_METADATA_COLLECTION", "1"); - assert!(run_clippy_for_package( - "clippy_lints", - &["-A", "unfulfilled_lint_expectations"] - )); - - // Check if cargo caching got in the way - if let Ok(file) = File::open(metadata_output_path) { - if let Ok(metadata) = file.metadata() { - if let Ok(last_modification) = metadata.modified() { - if last_modification > start_time { - // The output file has been modified. Most likely by a hungry - // metadata collection monster. So We'll return. - return; - } - } - } - } - - // Force cargo to invalidate the caches - filetime::set_file_mtime( - PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("clippy_lints/src/lib.rs"), - filetime::FileTime::now(), - ) - .unwrap(); - - // Running the collection again - assert!(run_clippy_for_package( - "clippy_lints", - &["-A", "unfulfilled_lint_expectations"] - )); -} - #[must_use] fn run_clippy_for_package(project: &str, args: &[&str]) -> bool { let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed index 3908411da827..0a9948428341 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed @@ -13,7 +13,7 @@ extern crate rustc_span; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; #[allow(unused)] use clippy_utils::{ - is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, path_res, }; @@ -22,8 +22,8 @@ use rustc_hir::LangItem; #[allow(unused)] use rustc_span::sym; -use rustc_hir::def_id::DefId; use rustc_hir::Expr; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs index 632e26215a4d..ba68de6c6d01 100644 --- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs +++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs @@ -13,7 +13,7 @@ extern crate rustc_span; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type}; #[allow(unused)] use clippy_utils::{ - is_expr_path_def_path, is_path_diagnostic_item, is_res_diagnostic_ctor, is_res_lang_ctor, is_trait_method, + is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, path_res, }; @@ -22,8 +22,8 @@ use rustc_hir::LangItem; #[allow(unused)] use rustc_span::sym; -use rustc_hir::def_id::DefId; use rustc_hir::Expr; +use rustc_hir::def_id::DefId; use rustc_lint::LateContext; use rustc_middle::ty::Ty; diff --git a/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs b/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs index a2e2b46c4269..61ae8de8e335 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs +++ b/src/tools/clippy/tests/ui-toml/disallowed_names_append/disallowed_names.rs @@ -1,4 +1,4 @@ -#[warn(clippy::disallowed_names)] +#![warn(clippy::disallowed_names)] fn main() { // `foo` is part of the default configuration diff --git a/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs b/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs index a2e2b46c4269..61ae8de8e335 100644 --- a/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs +++ b/src/tools/clippy/tests/ui-toml/disallowed_names_replace/disallowed_names.rs @@ -1,4 +1,4 @@ -#[warn(clippy::disallowed_names)] +#![warn(clippy::disallowed_names)] fn main() { // `foo` is part of the default configuration diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed index 5f4f007cf5c7..a6072111dc06 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.fixed @@ -2,7 +2,7 @@ use std::alloc as colla; use std::option::Option as Maybe; -use std::process::{exit as goodbye, Child as Kid}; +use std::process::{Child as Kid, exit as goodbye}; use std::thread::sleep as thread_sleep; #[rustfmt::skip] use std::{ diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs index f60058c86288..c2b61aab5b3c 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs @@ -2,7 +2,7 @@ use std::alloc as colla; use std::option::Option as Maybe; -use std::process::{exit as wrong_exit, Child as Kid}; +use std::process::{Child as Kid, exit as wrong_exit}; use std::thread::sleep; #[rustfmt::skip] use std::{ diff --git a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr index f66938c83fbf..d3bb07ec47fd 100644 --- a/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr +++ b/src/tools/clippy/tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.stderr @@ -1,8 +1,8 @@ error: this import should be renamed - --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:20 + --> tests/ui-toml/missing_enforced_import_rename/conf_missing_enforced_import_rename.rs:5:34 | -LL | use std::process::{exit as wrong_exit, Child as Kid}; - | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` +LL | use std::process::{Child as Kid, exit as wrong_exit}; + | ^^^^^^^^^^^^^^^^^^ help: try: `exit as goodbye` | = note: `-D clippy::missing-enforced-import-renames` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_enforced_import_renames)]` diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.rs b/src/tools/clippy/tests/ui-toml/panic/panic.rs index 618a37ddfc55..b6264c950e45 100644 --- a/src/tools/clippy/tests/ui-toml/panic/panic.rs +++ b/src/tools/clippy/tests/ui-toml/panic/panic.rs @@ -1,5 +1,6 @@ //@compile-flags: --test #![warn(clippy::panic)] +use std::panic::panic_any; fn main() { enum Enam { @@ -12,6 +13,10 @@ fn main() { } } +fn issue_13292() { + panic_any("should lint") +} + #[test] fn lonely_test() { enum Enam { diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.stderr b/src/tools/clippy/tests/ui-toml/panic/panic.stderr index bf7503e086c9..a034207d919f 100644 --- a/src/tools/clippy/tests/ui-toml/panic/panic.stderr +++ b/src/tools/clippy/tests/ui-toml/panic/panic.stderr @@ -1,5 +1,5 @@ error: `panic` should not be present in production code - --> tests/ui-toml/panic/panic.rs:11:14 + --> tests/ui-toml/panic/panic.rs:12:14 | LL | _ => panic!(""), | ^^^^^^^^^^ @@ -7,5 +7,11 @@ LL | _ => panic!(""), = note: `-D clippy::panic` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::panic)]` -error: aborting due to 1 previous error +error: `panic_any` should not be present in production code + --> tests/ui-toml/panic/panic.rs:17:5 + | +LL | panic_any("should lint") + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed index 49ee3ee17c70..058dbb77a320 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.fixed +++ b/src/tools/clippy/tests/ui/allow_attributes.fixed @@ -58,3 +58,10 @@ fn msrv_1_80() { #[allow(unused)] let x = 1; } + +#[deny(clippy::allow_attributes)] +fn deny_allow_attributes() -> Option { + let allow = None; + allow?; + Some(42) +} diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs index 854acf8348dc..6d94ce50e4c3 100644 --- a/src/tools/clippy/tests/ui/allow_attributes.rs +++ b/src/tools/clippy/tests/ui/allow_attributes.rs @@ -58,3 +58,10 @@ fn msrv_1_80() { #[allow(unused)] let x = 1; } + +#[deny(clippy::allow_attributes)] +fn deny_allow_attributes() -> Option { + let allow = None; + allow?; + Some(42) +} diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs index 86f6b2c5742a..334e7ddd9d23 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.rs @@ -15,7 +15,6 @@ use proc_macros::{external, with_span}; #[warn(deref_nullptr)] #[deny(deref_nullptr)] #[forbid(deref_nullptr)] - fn main() { external! { #[allow(dead_code)] diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr index 9bc3ca0f2afd..86d7845df041 100644 --- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr +++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr @@ -36,7 +36,7 @@ LL | #[expect(dead_code)] = help: try adding a reason at the end with `, reason = ".."` error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:47:5 + --> tests/ui/allow_attributes_without_reason.rs:46:5 | LL | #[allow(unused)] | ^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | #[allow(unused)] = help: try adding a reason at the end with `, reason = ".."` error: `allow` attribute without specifying a reason - --> tests/ui/allow_attributes_without_reason.rs:47:5 + --> tests/ui/allow_attributes_without_reason.rs:46:5 | LL | #[allow(unused)] | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs index 0838d064a5fa..3f2040730851 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.rs +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.rs @@ -2,7 +2,6 @@ #![feature(f128)] #![feature(f16)] - #![allow( clippy::assign_op_pattern, clippy::erasing_op, diff --git a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr index 78914667bf30..78b1aca4b8a4 100644 --- a/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr +++ b/src/tools/clippy/tests/ui/arithmetic_side_effects.stderr @@ -1,5 +1,5 @@ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:167:13 + --> tests/ui/arithmetic_side_effects.rs:166:13 | LL | let _ = 1f16 + 1f16; | ^^^^^^^^^^^ @@ -8,733 +8,733 @@ LL | let _ = 1f16 + 1f16; = help: to override `-D warnings` add `#[allow(clippy::arithmetic_side_effects)]` error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:170:13 + --> tests/ui/arithmetic_side_effects.rs:169:13 | LL | let _ = 1f128 + 1f128; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:308:5 + --> tests/ui/arithmetic_side_effects.rs:307:5 | LL | _n += 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:309:5 + --> tests/ui/arithmetic_side_effects.rs:308:5 | LL | _n += &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:310:5 + --> tests/ui/arithmetic_side_effects.rs:309:5 | LL | _n -= 1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:311:5 + --> tests/ui/arithmetic_side_effects.rs:310:5 | LL | _n -= &1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:312:5 + --> tests/ui/arithmetic_side_effects.rs:311:5 | LL | _n /= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:313:5 + --> tests/ui/arithmetic_side_effects.rs:312:5 | LL | _n /= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:314:5 + --> tests/ui/arithmetic_side_effects.rs:313:5 | LL | _n %= 0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:315:5 + --> tests/ui/arithmetic_side_effects.rs:314:5 | LL | _n %= &0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:316:5 + --> tests/ui/arithmetic_side_effects.rs:315:5 | LL | _n *= 2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:317:5 + --> tests/ui/arithmetic_side_effects.rs:316:5 | LL | _n *= &2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:318:5 + --> tests/ui/arithmetic_side_effects.rs:317:5 | LL | _n += -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:319:5 + --> tests/ui/arithmetic_side_effects.rs:318:5 | LL | _n += &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:320:5 + --> tests/ui/arithmetic_side_effects.rs:319:5 | LL | _n -= -1; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:321:5 + --> tests/ui/arithmetic_side_effects.rs:320:5 | LL | _n -= &-1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:322:5 + --> tests/ui/arithmetic_side_effects.rs:321:5 | LL | _n /= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:323:5 + --> tests/ui/arithmetic_side_effects.rs:322:5 | LL | _n /= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:324:5 + --> tests/ui/arithmetic_side_effects.rs:323:5 | LL | _n %= -0; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:325:5 + --> tests/ui/arithmetic_side_effects.rs:324:5 | LL | _n %= &-0; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:326:5 + --> tests/ui/arithmetic_side_effects.rs:325:5 | LL | _n *= -2; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:327:5 + --> tests/ui/arithmetic_side_effects.rs:326:5 | LL | _n *= &-2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:328:5 + --> tests/ui/arithmetic_side_effects.rs:327:5 | LL | _custom += Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:329:5 + --> tests/ui/arithmetic_side_effects.rs:328:5 | LL | _custom += &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:330:5 + --> tests/ui/arithmetic_side_effects.rs:329:5 | LL | _custom -= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:331:5 + --> tests/ui/arithmetic_side_effects.rs:330:5 | LL | _custom -= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:332:5 + --> tests/ui/arithmetic_side_effects.rs:331:5 | LL | _custom /= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:333:5 + --> tests/ui/arithmetic_side_effects.rs:332:5 | LL | _custom /= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:334:5 + --> tests/ui/arithmetic_side_effects.rs:333:5 | LL | _custom %= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:335:5 + --> tests/ui/arithmetic_side_effects.rs:334:5 | LL | _custom %= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:336:5 + --> tests/ui/arithmetic_side_effects.rs:335:5 | LL | _custom *= Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:337:5 + --> tests/ui/arithmetic_side_effects.rs:336:5 | LL | _custom *= &Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:338:5 + --> tests/ui/arithmetic_side_effects.rs:337:5 | LL | _custom >>= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:339:5 + --> tests/ui/arithmetic_side_effects.rs:338:5 | LL | _custom >>= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:340:5 + --> tests/ui/arithmetic_side_effects.rs:339:5 | LL | _custom <<= Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:341:5 + --> tests/ui/arithmetic_side_effects.rs:340:5 | LL | _custom <<= &Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:342:5 + --> tests/ui/arithmetic_side_effects.rs:341:5 | LL | _custom += -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:343:5 + --> tests/ui/arithmetic_side_effects.rs:342:5 | LL | _custom += &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:344:5 + --> tests/ui/arithmetic_side_effects.rs:343:5 | LL | _custom -= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:345:5 + --> tests/ui/arithmetic_side_effects.rs:344:5 | LL | _custom -= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:346:5 + --> tests/ui/arithmetic_side_effects.rs:345:5 | LL | _custom /= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:347:5 + --> tests/ui/arithmetic_side_effects.rs:346:5 | LL | _custom /= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:348:5 + --> tests/ui/arithmetic_side_effects.rs:347:5 | LL | _custom %= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:349:5 + --> tests/ui/arithmetic_side_effects.rs:348:5 | LL | _custom %= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:350:5 + --> tests/ui/arithmetic_side_effects.rs:349:5 | LL | _custom *= -Custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:351:5 + --> tests/ui/arithmetic_side_effects.rs:350:5 | LL | _custom *= &-Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:352:5 + --> tests/ui/arithmetic_side_effects.rs:351:5 | LL | _custom >>= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:353:5 + --> tests/ui/arithmetic_side_effects.rs:352:5 | LL | _custom >>= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:354:5 + --> tests/ui/arithmetic_side_effects.rs:353:5 | LL | _custom <<= -Custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:355:5 + --> tests/ui/arithmetic_side_effects.rs:354:5 | LL | _custom <<= &-Custom; | ^^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:358:10 + --> tests/ui/arithmetic_side_effects.rs:357:10 | LL | _n = _n + 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:359:10 + --> tests/ui/arithmetic_side_effects.rs:358:10 | LL | _n = _n + &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:360:10 + --> tests/ui/arithmetic_side_effects.rs:359:10 | LL | _n = 1 + _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:361:10 + --> tests/ui/arithmetic_side_effects.rs:360:10 | LL | _n = &1 + _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:362:10 + --> tests/ui/arithmetic_side_effects.rs:361:10 | LL | _n = _n - 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:363:10 + --> tests/ui/arithmetic_side_effects.rs:362:10 | LL | _n = _n - &1; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:364:10 + --> tests/ui/arithmetic_side_effects.rs:363:10 | LL | _n = 1 - _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:365:10 + --> tests/ui/arithmetic_side_effects.rs:364:10 | LL | _n = &1 - _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:366:10 + --> tests/ui/arithmetic_side_effects.rs:365:10 | LL | _n = _n / 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:367:10 + --> tests/ui/arithmetic_side_effects.rs:366:10 | LL | _n = _n / &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:368:10 + --> tests/ui/arithmetic_side_effects.rs:367:10 | LL | _n = _n % 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:369:10 + --> tests/ui/arithmetic_side_effects.rs:368:10 | LL | _n = _n % &0; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:370:10 + --> tests/ui/arithmetic_side_effects.rs:369:10 | LL | _n = _n * 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:371:10 + --> tests/ui/arithmetic_side_effects.rs:370:10 | LL | _n = _n * &2; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:372:10 + --> tests/ui/arithmetic_side_effects.rs:371:10 | LL | _n = 2 * _n; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:373:10 + --> tests/ui/arithmetic_side_effects.rs:372:10 | LL | _n = &2 * _n; | ^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:374:10 + --> tests/ui/arithmetic_side_effects.rs:373:10 | LL | _n = 23 + &85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:375:10 + --> tests/ui/arithmetic_side_effects.rs:374:10 | LL | _n = &23 + 85; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:376:10 + --> tests/ui/arithmetic_side_effects.rs:375:10 | LL | _n = &23 + &85; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:377:15 + --> tests/ui/arithmetic_side_effects.rs:376:15 | LL | _custom = _custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:378:15 + --> tests/ui/arithmetic_side_effects.rs:377:15 | LL | _custom = _custom + &_custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:379:15 + --> tests/ui/arithmetic_side_effects.rs:378:15 | LL | _custom = Custom + _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:380:15 + --> tests/ui/arithmetic_side_effects.rs:379:15 | LL | _custom = &Custom + _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:381:15 + --> tests/ui/arithmetic_side_effects.rs:380:15 | LL | _custom = _custom - Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:382:15 + --> tests/ui/arithmetic_side_effects.rs:381:15 | LL | _custom = _custom - &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:383:15 + --> tests/ui/arithmetic_side_effects.rs:382:15 | LL | _custom = Custom - _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:384:15 + --> tests/ui/arithmetic_side_effects.rs:383:15 | LL | _custom = &Custom - _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:385:15 + --> tests/ui/arithmetic_side_effects.rs:384:15 | LL | _custom = _custom / Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:386:15 + --> tests/ui/arithmetic_side_effects.rs:385:15 | LL | _custom = _custom / &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:387:15 + --> tests/ui/arithmetic_side_effects.rs:386:15 | LL | _custom = _custom % Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:388:15 + --> tests/ui/arithmetic_side_effects.rs:387:15 | LL | _custom = _custom % &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:389:15 + --> tests/ui/arithmetic_side_effects.rs:388:15 | LL | _custom = _custom * Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:390:15 + --> tests/ui/arithmetic_side_effects.rs:389:15 | LL | _custom = _custom * &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:391:15 + --> tests/ui/arithmetic_side_effects.rs:390:15 | LL | _custom = Custom * _custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:392:15 + --> tests/ui/arithmetic_side_effects.rs:391:15 | LL | _custom = &Custom * _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:393:15 + --> tests/ui/arithmetic_side_effects.rs:392:15 | LL | _custom = Custom + &Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:394:15 + --> tests/ui/arithmetic_side_effects.rs:393:15 | LL | _custom = &Custom + Custom; | ^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:395:15 + --> tests/ui/arithmetic_side_effects.rs:394:15 | LL | _custom = &Custom + &Custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:396:15 + --> tests/ui/arithmetic_side_effects.rs:395:15 | LL | _custom = _custom >> _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:397:15 + --> tests/ui/arithmetic_side_effects.rs:396:15 | LL | _custom = _custom >> &_custom; | ^^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:398:15 + --> tests/ui/arithmetic_side_effects.rs:397:15 | LL | _custom = Custom << _custom; | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:399:15 + --> tests/ui/arithmetic_side_effects.rs:398:15 | LL | _custom = &Custom << _custom; | ^^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:402:23 + --> tests/ui/arithmetic_side_effects.rs:401:23 | LL | _n.saturating_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:403:21 + --> tests/ui/arithmetic_side_effects.rs:402:21 | LL | _n.wrapping_div(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:404:21 + --> tests/ui/arithmetic_side_effects.rs:403:21 | LL | _n.wrapping_rem(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:405:28 + --> tests/ui/arithmetic_side_effects.rs:404:28 | LL | _n.wrapping_rem_euclid(0); | ^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:407:23 + --> tests/ui/arithmetic_side_effects.rs:406:23 | LL | _n.saturating_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:408:21 + --> tests/ui/arithmetic_side_effects.rs:407:21 | LL | _n.wrapping_div(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:409:21 + --> tests/ui/arithmetic_side_effects.rs:408:21 | LL | _n.wrapping_rem(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:410:28 + --> tests/ui/arithmetic_side_effects.rs:409:28 | LL | _n.wrapping_rem_euclid(_n); | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:413:10 + --> tests/ui/arithmetic_side_effects.rs:412:10 | LL | _n = -_n; | ^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:414:10 + --> tests/ui/arithmetic_side_effects.rs:413:10 | LL | _n = -&_n; | ^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:415:15 + --> tests/ui/arithmetic_side_effects.rs:414:15 | LL | _custom = -_custom; | ^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:416:15 + --> tests/ui/arithmetic_side_effects.rs:415:15 | LL | _custom = -&_custom; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:425:5 + --> tests/ui/arithmetic_side_effects.rs:424:5 | LL | 1 + i; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:426:5 + --> tests/ui/arithmetic_side_effects.rs:425:5 | LL | i * 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:427:5 + --> tests/ui/arithmetic_side_effects.rs:426:5 | LL | 1 % i / 2; | ^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:428:5 + --> tests/ui/arithmetic_side_effects.rs:427:5 | LL | i - 2 + 2 - i; | ^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:429:5 + --> tests/ui/arithmetic_side_effects.rs:428:5 | LL | -i; | ^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:440:5 + --> tests/ui/arithmetic_side_effects.rs:439:5 | LL | i += 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:441:5 + --> tests/ui/arithmetic_side_effects.rs:440:5 | LL | i -= 1; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:442:5 + --> tests/ui/arithmetic_side_effects.rs:441:5 | LL | i *= 2; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:444:5 + --> tests/ui/arithmetic_side_effects.rs:443:5 | LL | i /= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:446:5 + --> tests/ui/arithmetic_side_effects.rs:445:5 | LL | i /= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:447:5 + --> tests/ui/arithmetic_side_effects.rs:446:5 | LL | i /= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:449:5 + --> tests/ui/arithmetic_side_effects.rs:448:5 | LL | i %= 0; | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:451:5 + --> tests/ui/arithmetic_side_effects.rs:450:5 | LL | i %= var1; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:452:5 + --> tests/ui/arithmetic_side_effects.rs:451:5 | LL | i %= var2; | ^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:462:5 + --> tests/ui/arithmetic_side_effects.rs:461:5 | LL | 10 / a | ^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:516:9 + --> tests/ui/arithmetic_side_effects.rs:515:9 | LL | x / maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:520:9 + --> tests/ui/arithmetic_side_effects.rs:519:9 | LL | x % maybe_zero | ^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:531:5 + --> tests/ui/arithmetic_side_effects.rs:530:5 | LL | one.add_assign(1); | ^^^^^^^^^^^^^^^^^ error: arithmetic operation that can potentially result in unexpected side-effects - --> tests/ui/arithmetic_side_effects.rs:535:5 + --> tests/ui/arithmetic_side_effects.rs:534:5 | LL | one.sub_assign(1); | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs index 33cea4806814..a7d29cc239e5 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_not_x86.rs @@ -1,5 +1,4 @@ -//@ignore-target-i686 -//@ignore-target-x86 +//@ignore-target: i686 x86 //@needs-asm-support #[warn(clippy::inline_asm_x86_intel_syntax)] diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.rs b/src/tools/clippy/tests/ui/asm_syntax_x86.rs index 835943b43894..5ceaceb7527b 100644 --- a/src/tools/clippy/tests/ui/asm_syntax_x86.rs +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.rs @@ -1,6 +1,4 @@ -//@revisions: i686 x86_64 -//@[i686] only-target-i686 -//@[x86_64] only-target-x86_64 +//@only-target: i686 x86_64 #[warn(clippy::inline_asm_x86_intel_syntax)] mod warn_intel { diff --git a/src/tools/clippy/tests/ui/asm_syntax_x86.stderr b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr new file mode 100644 index 000000000000..1911ef66e239 --- /dev/null +++ b/src/tools/clippy/tests/ui/asm_syntax_x86.stderr @@ -0,0 +1,70 @@ +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:8:9 + | +LL | asm!(""); + | ^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + = note: `-D clippy::inline-asm-x86-intel-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_intel_syntax)]` + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:10:9 + | +LL | asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:12:9 + | +LL | asm!("", options(nostack)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:18:5 + | +LL | global_asm!(""); + | ^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: Intel x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:20:5 + | +LL | global_asm!("", options()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use AT&T x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:33:9 + | +LL | asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + = note: `-D clippy::inline-asm-x86-att-syntax` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inline_asm_x86_att_syntax)]` + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:35:9 + | +LL | asm!("", options(nostack, att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: AT&T x86 assembly syntax used + --> tests/ui/asm_syntax_x86.rs:41:5 + | +LL | global_asm!("", options(att_syntax)); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: use Intel x86 assembly syntax + +error: aborting due to 8 previous errors + diff --git a/src/tools/clippy/tests/ui/auxiliary/external_item.rs b/src/tools/clippy/tests/ui/auxiliary/external_item.rs new file mode 100644 index 000000000000..ca4bc369e449 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/external_item.rs @@ -0,0 +1,7 @@ +pub struct _ExternalStruct {} + +impl _ExternalStruct { + pub fn _foo(self) {} +} + +pub fn _exernal_foo() {} diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs index f6fdebaf2527..e72d6b6ceadf 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_attr.rs @@ -11,8 +11,8 @@ use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::token::Star; use syn::{ - parse_macro_input, parse_quote, FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, - PatType, Signature, TraitItem, Type, Visibility, + FnArg, ImplItem, ItemFn, ItemImpl, ItemStruct, ItemTrait, Lifetime, Pat, PatIdent, PatType, Signature, TraitItem, + Type, Visibility, parse_macro_input, parse_quote, }; #[proc_macro_attribute] diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs index 4c3df4722690..bd90042c1da8 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs @@ -5,7 +5,7 @@ extern crate proc_macro; -use proc_macro::{quote, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree, quote}; #[proc_macro_derive(DeriveSomething)] pub fn derive(_: TokenStream) -> TokenStream { diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs index 79e8eff3aa10..3d6f164d3583 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_suspicious_else_formatting.rs @@ -1,5 +1,5 @@ extern crate proc_macro; -use proc_macro::{token_stream, Delimiter, Group, Ident, Span, TokenStream, TokenTree}; +use proc_macro::{Delimiter, Group, Ident, Span, TokenStream, TokenTree, token_stream}; fn read_ident(iter: &mut token_stream::IntoIter) -> Ident { match iter.next() { diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs index ed7412f7c407..1a2a4ec23114 100644 --- a/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs +++ b/src/tools/clippy/tests/ui/auxiliary/proc_macros.rs @@ -5,9 +5,9 @@ extern crate proc_macro; use core::mem; -use proc_macro::token_stream::IntoIter; use proc_macro::Delimiter::{self, Brace, Parenthesis}; use proc_macro::Spacing::{self, Alone, Joint}; +use proc_macro::token_stream::IntoIter; use proc_macro::{Group, Ident, Literal, Punct, Span, TokenStream, TokenTree as TT}; use syn::spanned::Spanned; diff --git a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs index 452d1b198133..de220505c3e0 100644 --- a/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/borrow_interior_mutable_const/others.rs @@ -5,8 +5,8 @@ use std::borrow::Cow; use std::cell::{Cell, UnsafeCell}; use std::fmt::Display; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Once; +use std::sync::atomic::{AtomicUsize, Ordering}; const ATOMIC: AtomicUsize = AtomicUsize::new(5); const CELL: Cell = Cell::new(6); diff --git a/src/tools/clippy/tests/ui/cast_alignment.rs b/src/tools/clippy/tests/ui/cast_alignment.rs index 98ef5e36f948..72f5d4268cc1 100644 --- a/src/tools/clippy/tests/ui/cast_alignment.rs +++ b/src/tools/clippy/tests/ui/cast_alignment.rs @@ -2,16 +2,16 @@ #![feature(rustc_private)] #![feature(core_intrinsics)] -extern crate libc; - -#[warn(clippy::cast_ptr_alignment)] -#[allow( +#![warn(clippy::cast_ptr_alignment)] +#![allow( clippy::no_effect, clippy::unnecessary_operation, clippy::cast_lossless, clippy::borrow_as_ptr )] +extern crate libc; + fn main() { /* These should be warned against */ diff --git a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs index ec45d635c172..913aab727471 100644 --- a/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs +++ b/src/tools/clippy/tests/ui/cmp_owned/without_suggestion.rs @@ -1,5 +1,5 @@ -#[allow(clippy::unnecessary_operation)] -#[allow(clippy::implicit_clone)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::implicit_clone)] fn main() { let x = &Baz; diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.fixed b/src/tools/clippy/tests/ui/collapsible_else_if.fixed index 3b410b2f17b8..c2d76146c641 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.fixed +++ b/src/tools/clippy/tests/ui/collapsible_else_if.fixed @@ -1,9 +1,7 @@ #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] -#[warn(clippy::collapsible_else_if)] - fn main() { let x = "hello"; let y = "world"; @@ -76,7 +74,6 @@ fn main() { } #[rustfmt::skip] -#[allow(dead_code)] fn issue_7318() { if true { println!("I've been resolved!") }else if false {} diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.rs b/src/tools/clippy/tests/ui/collapsible_else_if.rs index 772ef6f9fc60..3579e46cd447 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.rs +++ b/src/tools/clippy/tests/ui/collapsible_else_if.rs @@ -1,9 +1,7 @@ #![allow(clippy::assertions_on_constants, clippy::equatable_if_let, clippy::needless_if)] +#![warn(clippy::collapsible_if, clippy::collapsible_else_if)] #[rustfmt::skip] -#[warn(clippy::collapsible_if)] -#[warn(clippy::collapsible_else_if)] - fn main() { let x = "hello"; let y = "world"; @@ -90,7 +88,6 @@ fn main() { } #[rustfmt::skip] -#[allow(dead_code)] fn issue_7318() { if true { println!("I've been resolved!") }else{ diff --git a/src/tools/clippy/tests/ui/collapsible_else_if.stderr b/src/tools/clippy/tests/ui/collapsible_else_if.stderr index dc19d90b4d13..395c2dcf68dc 100644 --- a/src/tools/clippy/tests/ui/collapsible_else_if.stderr +++ b/src/tools/clippy/tests/ui/collapsible_else_if.stderr @@ -1,5 +1,5 @@ error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:13:12 + --> tests/ui/collapsible_else_if.rs:11:12 | LL | } else { | ____________^ @@ -19,7 +19,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:21:12 + --> tests/ui/collapsible_else_if.rs:19:12 | LL | } else { | ____________^ @@ -37,7 +37,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:29:12 + --> tests/ui/collapsible_else_if.rs:27:12 | LL | } else { | ____________^ @@ -60,7 +60,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:40:12 + --> tests/ui/collapsible_else_if.rs:38:12 | LL | } else { | ____________^ @@ -83,7 +83,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:51:12 + --> tests/ui/collapsible_else_if.rs:49:12 | LL | } else { | ____________^ @@ -106,7 +106,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:62:12 + --> tests/ui/collapsible_else_if.rs:60:12 | LL | } else { | ____________^ @@ -129,7 +129,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:73:12 + --> tests/ui/collapsible_else_if.rs:71:12 | LL | } else { | ____________^ @@ -152,7 +152,7 @@ LL + } | error: this `else { if .. }` block can be collapsed - --> tests/ui/collapsible_else_if.rs:96:10 + --> tests/ui/collapsible_else_if.rs:93:10 | LL | }else{ | __________^ diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.fixed b/src/tools/clippy/tests/ui/comparison_to_empty.fixed index e102b13a7618..a2a3dd9086d4 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.fixed +++ b/src/tools/clippy/tests/ui/comparison_to_empty.fixed @@ -33,4 +33,12 @@ fn main() { if let [0] = &*s && s == [0] {} + + // Also lint the `PartialEq` methods + let s = String::new(); + let _ = s.is_empty(); + let _ = !s.is_empty(); + let v = vec![0]; + let _ = v.is_empty(); + let _ = !v.is_empty(); } diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.rs b/src/tools/clippy/tests/ui/comparison_to_empty.rs index 69a6c967d382..7c5689a4bbec 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.rs +++ b/src/tools/clippy/tests/ui/comparison_to_empty.rs @@ -33,4 +33,12 @@ fn main() { if let [0] = &*s && s == [0] {} + + // Also lint the `PartialEq` methods + let s = String::new(); + let _ = s.eq(""); + let _ = s.ne(""); + let v = vec![0]; + let _ = v.eq(&[]); + let _ = v.ne(&[]); } diff --git a/src/tools/clippy/tests/ui/comparison_to_empty.stderr b/src/tools/clippy/tests/ui/comparison_to_empty.stderr index 6b027459ed34..2ee0efc7dbb1 100644 --- a/src/tools/clippy/tests/ui/comparison_to_empty.stderr +++ b/src/tools/clippy/tests/ui/comparison_to_empty.stderr @@ -55,5 +55,29 @@ error: comparison to empty slice LL | && s == [] | ^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` -error: aborting due to 9 previous errors +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:39:13 + | +LL | let _ = s.eq(""); + | ^^^^^^^^ help: using `is_empty` is clearer and more explicit: `s.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:40:13 + | +LL | let _ = s.ne(""); + | ^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!s.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:42:13 + | +LL | let _ = v.eq(&[]); + | ^^^^^^^^^ help: using `is_empty` is clearer and more explicit: `v.is_empty()` + +error: comparison to empty slice + --> tests/ui/comparison_to_empty.rs:43:13 + | +LL | let _ = v.ne(&[]); + | ^^^^^^^^^ help: using `!is_empty` is clearer and more explicit: `!v.is_empty()` + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs index 948deba3ea6e..fec16671eeb3 100644 --- a/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs +++ b/src/tools/clippy/tests/ui/crashes/associated-constant-ice.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/1698 +// Test for https://github.com/rust-lang/rust-clippy/issues/1698 pub trait Trait { const CONSTANT: u8; diff --git a/src/tools/clippy/tests/ui/crashes/cc_seme.rs b/src/tools/clippy/tests/ui/crashes/cc_seme.rs index 98588be9cf82..98897d6d7aaf 100644 --- a/src/tools/clippy/tests/ui/crashes/cc_seme.rs +++ b/src/tools/clippy/tests/ui/crashes/cc_seme.rs @@ -1,6 +1,4 @@ -#[allow(dead_code)] - -/// Test for https://github.com/rust-lang/rust-clippy/issues/478 +// Test for https://github.com/rust-lang/rust-clippy/issues/478 enum Baz { One, diff --git a/src/tools/clippy/tests/ui/crashes/ice-11230.rs b/src/tools/clippy/tests/ui/crashes/ice-11230.rs index 5761882273e0..94044e9435ed 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-11230.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-11230.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/11230 +// Test for https://github.com/rust-lang/rust-clippy/issues/11230 fn main() { const A: &[for<'a> fn(&'a ())] = &[]; diff --git a/src/tools/clippy/tests/ui/crashes/ice-1588.rs b/src/tools/clippy/tests/ui/crashes/ice-1588.rs index b0a3d11bce46..9ec093721c17 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1588.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1588.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1588 +// Test for https://github.com/rust-lang/rust-clippy/issues/1588 fn main() { match 1 { diff --git a/src/tools/clippy/tests/ui/crashes/ice-1969.rs b/src/tools/clippy/tests/ui/crashes/ice-1969.rs index 96a8fe6c24d5..eb901c767294 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-1969.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-1969.rs @@ -1,6 +1,6 @@ #![allow(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1969 +// Test for https://github.com/rust-lang/rust-clippy/issues/1969 fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-2499.rs b/src/tools/clippy/tests/ui/crashes/ice-2499.rs index 45b3b1869dde..732f331ad145 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2499.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2499.rs @@ -1,8 +1,8 @@ #![allow(dead_code, clippy::char_lit_as_u8, clippy::needless_bool)] -/// Should not trigger an ICE in `SpanlessHash` / `consts::constant` -/// -/// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 +// Should not trigger an ICE in `SpanlessHash` / `consts::constant` +// +// Issue: https://github.com/rust-lang/rust-clippy/issues/2499 fn f(s: &[u8]) -> bool { let t = s[0] as char; diff --git a/src/tools/clippy/tests/ui/crashes/ice-2594.rs b/src/tools/clippy/tests/ui/crashes/ice-2594.rs index 3f3986b6fc69..dddf860bd178 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2594.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2594.rs @@ -3,7 +3,6 @@ /// Should not trigger an ICE in `SpanlessHash` / `consts::constant` /// /// Issue: https://github.com/rust-lang/rust-clippy/issues/2594 - fn spanless_hash_ice() { let txt = "something"; let empty_header: [u8; 1] = [1; 1]; diff --git a/src/tools/clippy/tests/ui/crashes/ice-2727.rs b/src/tools/clippy/tests/ui/crashes/ice-2727.rs index 56024abc8f58..59fb9b04b86d 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2727.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2727.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2727 +// Test for https://github.com/rust-lang/rust-clippy/issues/2727 pub fn f(new: fn()) { new(); diff --git a/src/tools/clippy/tests/ui/crashes/ice-2760.rs b/src/tools/clippy/tests/ui/crashes/ice-2760.rs index 61ef24804986..5f7d91abf995 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2760.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2760.rs @@ -5,10 +5,10 @@ dead_code )] -/// This should not compile-fail with: -/// -/// error[E0277]: the trait bound `T: Foo` is not satisfied -// See rust-lang/rust-clippy#2760. +// This should not compile-fail with: +// +// error[E0277]: the trait bound `T: Foo` is not satisfied +// See https://github.com/rust-lang/rust-clippy/issues/2760 trait Foo { type Bar; diff --git a/src/tools/clippy/tests/ui/crashes/ice-2862.rs b/src/tools/clippy/tests/ui/crashes/ice-2862.rs index 8326e3663b05..2573b571f55c 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2862.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2862.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/2862 +// Test for https://github.com/rust-lang/rust-clippy/issues/2862 pub trait FooMap { fn map B>(&self, f: F) -> B; diff --git a/src/tools/clippy/tests/ui/crashes/ice-2865.rs b/src/tools/clippy/tests/ui/crashes/ice-2865.rs index c62981396016..28363707acca 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-2865.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-2865.rs @@ -1,6 +1,6 @@ #![allow(dead_code, clippy::extra_unused_lifetimes)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2865 +// Test for https://github.com/rust-lang/rust-clippy/issues/2865 struct Ice { size: String, diff --git a/src/tools/clippy/tests/ui/crashes/ice-3151.rs b/src/tools/clippy/tests/ui/crashes/ice-3151.rs index 268ba86fc7aa..f88a26cb4859 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3151.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3151.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/3151 +// Test for https://github.com/rust-lang/rust-clippy/issues/3151 #[derive(Clone)] pub struct HashMap { diff --git a/src/tools/clippy/tests/ui/crashes/ice-3462.rs b/src/tools/clippy/tests/ui/crashes/ice-3462.rs index 21cd9d337cdd..ccd617e305da 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3462.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3462.rs @@ -2,7 +2,7 @@ #![allow(clippy::disallowed_names, clippy::equatable_if_let, clippy::needless_if)] #![allow(unused)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/3462 +// Test for https://github.com/rust-lang/rust-clippy/issues/3462 enum Foo { Bar, diff --git a/src/tools/clippy/tests/ui/crashes/ice-3747.rs b/src/tools/clippy/tests/ui/crashes/ice-3747.rs index cdf018cbc88d..44b1d7ed1b2e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3747.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3747.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/3747 +// Test for https://github.com/rust-lang/rust-clippy/issues/3747 macro_rules! a { ( $pub:tt $($attr:tt)* ) => { diff --git a/src/tools/clippy/tests/ui/crashes/ice-3969.rs b/src/tools/clippy/tests/ui/crashes/ice-3969.rs index d5676cbd91d1..ac09ce08753a 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-3969.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-3969.rs @@ -1,7 +1,7 @@ // https://github.com/rust-lang/rust-clippy/issues/3969 // used to crash: error: internal compiler error: // src/librustc_traits/normalize_erasing_regions.rs:43: could not fully normalize `::Item test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs +// std::iter::Iterator>::Item` test from rustc ./ui/trivial-bounds/trivial-bounds-inconsistent.rs // Check that tautalogically false bounds are accepted, and are used // in type inference. @@ -18,7 +18,7 @@ struct Dst { struct TwoStrs(str, str) where str: Sized; -//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetim +//~^ ERROR: trait bound str: std::marker::Sized does not depend on any type or lifetime //~| NOTE: `-D trivial-bounds` implied by `-D warnings` fn unsized_local() diff --git a/src/tools/clippy/tests/ui/crashes/ice-6251.rs b/src/tools/clippy/tests/ui/crashes/ice-6251.rs index a81137a64655..73e919b6dd2e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-6251.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-6251.rs @@ -1,5 +1,5 @@ // originally from glacier/fixed/77329.rs -// assertion failed: `(left == right) ; different DefIds +// assertion failed: `(left == right)` ; different DefIds //@no-rustfix fn bug() -> impl Iterator { std::iter::empty() diff --git a/src/tools/clippy/tests/ui/crashes/ice-700.rs b/src/tools/clippy/tests/ui/crashes/ice-700.rs index 0cbceedbd6bd..5e004b94330e 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-700.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-700.rs @@ -1,6 +1,6 @@ #![deny(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/700 +// Test for https://github.com/rust-lang/rust-clippy/issues/700 fn core() {} diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs index a2683b3ce340..ccf6d7ff94f2 100644 --- a/src/tools/clippy/tests/ui/crashes/ice-7410.rs +++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs @@ -1,6 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple -//@ignore-target-windows +//@ignore-target: apple windows #![feature(lang_items, start, libc)] #![no_std] diff --git a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs index 30e4b11ec0bd..c0671eaff145 100644 --- a/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs +++ b/src/tools/clippy/tests/ui/crashes/ice_exact_size.rs @@ -1,6 +1,6 @@ #![deny(clippy::all)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/1336 +// Test for https://github.com/rust-lang/rust-clippy/issues/1336 #[allow(dead_code)] struct Foo; diff --git a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs index 2f913292995e..a900fe5e6bc6 100644 --- a/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs +++ b/src/tools/clippy/tests/ui/crashes/if_same_then_else.rs @@ -1,7 +1,7 @@ #![allow(clippy::comparison_chain)] #![deny(clippy::if_same_then_else)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2426 +// Test for https://github.com/rust-lang/rust-clippy/issues/2426 fn main() {} diff --git a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs index aeb27b5ba8c2..800a5a383f62 100644 --- a/src/tools/clippy/tests/ui/crashes/inherent_impl.rs +++ b/src/tools/clippy/tests/ui/crashes/inherent_impl.rs @@ -1,6 +1,6 @@ #![deny(clippy::multiple_inherent_impl)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/4578 +// Test for https://github.com/rust-lang/rust-clippy/issues/4578 macro_rules! impl_foo { ($struct:ident) => { diff --git a/src/tools/clippy/tests/ui/crashes/issue-825.rs b/src/tools/clippy/tests/ui/crashes/issue-825.rs index 05696e3d7d56..e8b455a0ec67 100644 --- a/src/tools/clippy/tests/ui/crashes/issue-825.rs +++ b/src/tools/clippy/tests/ui/crashes/issue-825.rs @@ -1,6 +1,6 @@ #![allow(warnings)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/825 +// Test for https://github.com/rust-lang/rust-clippy/issues/825 // this should compile in a reasonable amount of time fn rust_type_id(name: &str) { diff --git a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs index 94c939665e61..626179c00155 100644 --- a/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs +++ b/src/tools/clippy/tests/ui/crashes/match_same_arms_const.rs @@ -1,6 +1,6 @@ #![deny(clippy::match_same_arms)] -/// Test for https://github.com/rust-lang/rust-clippy/issues/2427 +// Test for https://github.com/rust-lang/rust-clippy/issues/2427 const PRICE_OF_SWEETS: u32 = 5; const PRICE_OF_KINDNESS: u32 = 0; diff --git a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr index 90076d4338a4..284795700069 100644 --- a/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr +++ b/src/tools/clippy/tests/ui/crashes/needless_pass_by_value-w-late-bound.stderr @@ -4,7 +4,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn test(x: Foo<'_>) {} | ^^^^^^^ help: consider taking a reference instead: `&Foo<'_>` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/crashes/needless_pass_by_value-w-late-bound.rs:5:1 | LL | struct Foo<'a>(&'a [(); 100]); diff --git a/src/tools/clippy/tests/ui/crashes/returns.rs b/src/tools/clippy/tests/ui/crashes/returns.rs index 8021ed4607dd..91cdb5306c89 100644 --- a/src/tools/clippy/tests/ui/crashes/returns.rs +++ b/src/tools/clippy/tests/ui/crashes/returns.rs @@ -1,4 +1,4 @@ -/// Test for https://github.com/rust-lang/rust-clippy/issues/1346 +// Test for https://github.com/rust-lang/rust-clippy/issues/1346 #[deny(warnings)] fn cfg_return() -> i32 { diff --git a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs index aa76688d8010..5d853d97bc35 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/entrypoint_recursion.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(rustc_attrs)] diff --git a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs index 32eba9695920..9e5b2a489034 100644 --- a/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs +++ b/src/tools/clippy/tests/ui/crate_level_checks/no_std_main_recursion.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![feature(lang_items, start, libc)] #![no_std] diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs index 9dafad8b784b..0dccf18c4930 100644 --- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs +++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs @@ -4,8 +4,8 @@ use std::borrow::Cow; use std::cell::Cell; use std::fmt::Display; use std::ptr; -use std::sync::atomic::AtomicUsize; use std::sync::Once; +use std::sync::atomic::AtomicUsize; const ATOMIC: AtomicUsize = AtomicUsize::new(5); //~ ERROR: interior mutable const CELL: Cell = Cell::new(6); //~ ERROR: interior mutable diff --git a/src/tools/clippy/tests/ui/def_id_nocore.rs b/src/tools/clippy/tests/ui/def_id_nocore.rs index 190d636ebf34..c9650312db87 100644 --- a/src/tools/clippy/tests/ui/def_id_nocore.rs +++ b/src/tools/clippy/tests/ui/def_id_nocore.rs @@ -1,4 +1,4 @@ -//@ignore-target-apple +//@ignore-target: apple #![feature(no_core, lang_items, start)] #![no_core] diff --git a/src/tools/clippy/tests/ui/diverging_sub_expression.rs b/src/tools/clippy/tests/ui/diverging_sub_expression.rs index e0acf050949a..1abba60fd34a 100644 --- a/src/tools/clippy/tests/ui/diverging_sub_expression.rs +++ b/src/tools/clippy/tests/ui/diverging_sub_expression.rs @@ -67,3 +67,9 @@ fn foobar() { }; } } + +#[allow(unused)] +fn ignore_todo() { + let x: u32 = todo!(); + println!("{x}"); +} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed deleted file mode 100644 index 1aaa26afe7f2..000000000000 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed +++ /dev/null @@ -1,47 +0,0 @@ -// https://github.com/rust-lang/rust-clippy/issues/12917 -#![warn(clippy::doc_lazy_continuation)] - -/// This is a constant. -/// -/// The meaning of which should not be explained. -pub const A: i32 = 42; - -/// This is another constant, no longer used. -/// -/// This block of documentation has a long -/// explanation and derivation to explain -/// why it is what it is, and how it's used. -/// -/// It is left here for historical reasons, and -/// for reference. -/// -/// Reasons it's great: -/// - First reason -/// - Second reason -/// -//pub const B: i32 = 1337; - -/// This is yet another constant. -/// -/// This has a similar fate as `B`. -/// -/// Reasons it's useful: -/// 1. First reason -/// 2. Second reason -/// -//pub const C: i32 = 8008; - -/// This is still in use. -pub const D: i32 = 20; - -/// > blockquote code path -/// - -/// bottom text -pub const E: i32 = 20; - -/// > blockquote code path -/// -#[repr(C)] -/// bottom text -pub struct Foo(i32); diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs deleted file mode 100644 index e1ab8fc83892..000000000000 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs +++ /dev/null @@ -1,43 +0,0 @@ -// https://github.com/rust-lang/rust-clippy/issues/12917 -#![warn(clippy::doc_lazy_continuation)] - -/// This is a constant. -/// -/// The meaning of which should not be explained. -pub const A: i32 = 42; - -/// This is another constant, no longer used. -/// -/// This block of documentation has a long -/// explanation and derivation to explain -/// why it is what it is, and how it's used. -/// -/// It is left here for historical reasons, and -/// for reference. -/// -/// Reasons it's great: -/// - First reason -/// - Second reason -//pub const B: i32 = 1337; - -/// This is yet another constant. -/// -/// This has a similar fate as `B`. -/// -/// Reasons it's useful: -/// 1. First reason -/// 2. Second reason -//pub const C: i32 = 8008; - -/// This is still in use. -pub const D: i32 = 20; - -/// > blockquote code path - -/// bottom text -pub const E: i32 = 20; - -/// > blockquote code path -#[repr(C)] -/// bottom text -pub struct Foo(i32); diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr deleted file mode 100644 index 854906a74741..000000000000 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr +++ /dev/null @@ -1,56 +0,0 @@ -error: doc list item without indentation - --> tests/ui/doc/doc_lazy_blank_line.rs:23:5 - | -LL | /// This is yet another constant. - | ^ - | - = help: if this is intended to be part of the list, indent 3 spaces - = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// - Second reason -LL + /// - | - -error: doc list item without indentation - --> tests/ui/doc/doc_lazy_blank_line.rs:32:5 - | -LL | /// This is still in use. - | ^ - | - = help: if this is intended to be part of the list, indent 4 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// 2. Second reason -LL + /// - | - -error: doc quote line without `>` marker - --> tests/ui/doc/doc_lazy_blank_line.rs:37:5 - | -LL | /// bottom text - | ^ - | - = help: if this not intended to be a quote at all, escape it with `\>` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// > blockquote code path -LL + /// - | - -error: doc quote line without `>` marker - --> tests/ui/doc/doc_lazy_blank_line.rs:42:5 - | -LL | /// bottom text - | ^ - | - = help: if this not intended to be a quote at all, escape it with `\>` -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// > blockquote code path -LL + /// - | - -error: aborting due to 4 previous errors - diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed index ea59ae4c01c9..da537518a2b5 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed @@ -7,9 +7,8 @@ fn one() {} /// 1. first line /// lazy list continuations don't make warnings with this lint -/// //~^ ERROR: doc list item without indentation -/// because they don't have the +/// because they don't have the //~^ ERROR: doc list item without indentation fn two() {} @@ -20,9 +19,8 @@ fn three() {} /// - first line /// lazy list continuations don't make warnings with this lint -/// //~^ ERROR: doc list item without indentation -/// because they don't have the +/// because they don't have the //~^ ERROR: doc list item without indentation fn four() {} @@ -33,9 +31,8 @@ fn five() {} /// - - first line /// this will warn on the lazy continuation -/// //~^ ERROR: doc list item without indentation -/// and so should this +/// and so should this //~^ ERROR: doc list item without indentation fn six() {} diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr index 52aa74df8948..b38f43b7555f 100644 --- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr +++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr @@ -30,12 +30,11 @@ error: doc list item without indentation LL | /// because they don't have the | ^ | - = help: if this is intended to be part of the list, indent 3 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// lazy list continuations don't make warnings with this lint -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// because they don't have the + | +++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:16:5 @@ -67,12 +66,11 @@ error: doc list item without indentation LL | /// because they don't have the | ^ | - = help: if this is intended to be part of the list, indent 4 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// lazy list continuations don't make warnings with this lint -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// because they don't have the + | ++++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:28:5 @@ -104,12 +102,11 @@ error: doc list item without indentation LL | /// and so should this | ^^^^ | - = help: if this is intended to be part of the list, indent 2 spaces -help: if this should be its own paragraph, add a blank doc comment line - | -LL ~ /// this will warn on the lazy continuation -LL + /// + = help: if this is supposed to be its own paragraph, add a blank line +help: indent this line | +LL | /// and so should this + | ++ error: doc list item without indentation --> tests/ui/doc/doc_lazy_list.rs:56:5 diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs index 118f6e4a34c0..a725538436cb 100644 --- a/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.rs @@ -1,5 +1,4 @@ #![warn(clippy::duplicate_underscore_argument)] -#[allow(dead_code, unused)] fn join_the_dark_side(darth: i32, _darth: i32) {} //~^ ERROR: `darth` already exists, having another argument having almost the same name ma diff --git a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr index 40a24b823d11..74979b157882 100644 --- a/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr +++ b/src/tools/clippy/tests/ui/duplicate_underscore_argument.stderr @@ -1,5 +1,5 @@ error: `darth` already exists, having another argument having almost the same name makes code comprehension and documentation more difficult - --> tests/ui/duplicate_underscore_argument.rs:4:23 + --> tests/ui/duplicate_underscore_argument.rs:3:23 | LL | fn join_the_dark_side(darth: i32, _darth: i32) {} | ^^^^^ diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs index 97cf4a69682d..874f5d22075c 100644 --- a/src/tools/clippy/tests/ui/duplicated_attributes.rs +++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs @@ -27,4 +27,8 @@ trait Abc {} #[proc_macro_attr::duplicated_attr()] // Should not warn! fn babar() {} +#[allow(missing_docs, reason = "library for internal use only")] +#[allow(exported_private_dependencies, reason = "library for internal use only")] +fn duplicate_reason() {} + fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed new file mode 100644 index 000000000000..fd6a94b6a80c --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.1.fixed @@ -0,0 +1,135 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +/// Meant to be an +/// inner doc comment +/// for the crate +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + /// Meant to be an + /// inner doc comment + /// for the module + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning +/** This is also a doc comment and is part of the warning + */ +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + /// docs for `old_code` + // fn old_code() {} + fn new_code() {} + + //~vv empty_line_after_doc_comments + /// Docs + /// for OldA + // struct OldA; + /// Docs + /// for OldB + // struct OldB; + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /** + * Meant to be inner doc comment + */ + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /** + * Docs for `old_code` + */ + /* fn old_code() {} */ + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + /// Docs for `old_code2` + /* fn old_code2() {} */ + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed new file mode 100644 index 000000000000..7a57dcd92332 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.2.fixed @@ -0,0 +1,144 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +//! Meant to be an +//! inner doc comment +//! for the crate + +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + //! Meant to be an + //! inner doc comment + //! for the module + + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + /// + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning +/** This is also a doc comment and is part of the warning + */ +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + // /// docs for `old_code` + // fn old_code() {} + + fn new_code() {} + + //~vv empty_line_after_doc_comments + // /// Docs + // /// for OldA + // struct OldA; + + // /// Docs + // /// for OldB + // struct OldB; + + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /*! + * Meant to be inner doc comment + */ + + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /* + * Docs for `old_code` + */ + /* fn old_code() {} */ + + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + // /// Docs for `old_code2` + /* fn old_code2() {} */ + + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs new file mode 100644 index 000000000000..1da761a5c3d5 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs @@ -0,0 +1,147 @@ +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~vvv empty_line_after_doc_comments +/// Meant to be an +/// inner doc comment +/// for the crate + +fn first_in_crate() {} + +mod m { + //~vvv empty_line_after_doc_comments + /// Meant to be an + /// inner doc comment + /// for the module + + fn first_in_module() {} +} + +mod some_mod { + //! This doc comment should *NOT* produce a warning + + mod some_inner_mod { + fn some_noop() {} + } + + //~v empty_line_after_doc_comments + /// # Indented + + /// Blank line + fn indented() {} +} + +//~v empty_line_after_doc_comments +/// This should produce a warning + +fn with_doc_and_newline() {} + +// This should *NOT* produce a warning +#[crate_type = "lib"] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_doc_comments +/// This doc comment should produce a warning + +/** This is also a doc comment and is part of the warning + */ + +#[allow(non_camel_case_types)] +#[allow(missing_docs)] +#[allow(dead_code)] +fn three_attributes() {} + +mod misattributed { + //~v empty_line_after_doc_comments + /// docs for `old_code` + // fn old_code() {} + + fn new_code() {} + + //~vv empty_line_after_doc_comments + /// Docs + /// for OldA + // struct OldA; + + /// Docs + /// for OldB + // struct OldB; + + /// Docs + /// for Multiple + #[allow(dead_code)] + struct Multiple; +} + +mod block_comments { + //~v empty_line_after_doc_comments + /** + * Meant to be inner doc comment + */ + + fn first_in_module() {} + + //~v empty_line_after_doc_comments + /** + * Docs for `old_code` + */ + /* fn old_code() {} */ + + /** + * Docs for `new_code` + */ + fn new_code() {} + + //~v empty_line_after_doc_comments + /// Docs for `old_code2` + /* fn old_code2() {} */ + + /// Docs for `new_code2` + fn new_code2() {} +} + +// This should *NOT* produce a warning +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +// This should *NOT* produce a warning +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +/// Should not lint +// some line comment +/// gaps without an empty line +struct LineComment; + +/// This should *NOT* produce a warning because the empty line is inside a block comment +/* + +*/ +pub struct EmptyInBlockComment; + +/// This should *NOT* produce a warning +/* test */ +pub struct BlockComment; + +/// Ignore the empty line inside a cfg_attr'd out attribute +#[cfg_attr(any(), multiline( + foo = 1 + + bar = 2 +))] +fn empty_line_in_cfg_attr() {} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr new file mode 100644 index 000000000000..c238b4c9a17f --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr @@ -0,0 +1,176 @@ +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:6:1 + | +LL | / /// for the crate +LL | | + | |_ +LL | fn first_in_crate() {} + | ------------------- the comment documents this function + | + = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]` + = help: if the empty line is unintentional remove it +help: if the comment should document the crate use an inner doc comment + | +LL ~ //! Meant to be an +LL ~ //! inner doc comment +LL ~ //! for the crate + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:14:5 + | +LL | / /// for the module +LL | | + | |_ +LL | fn first_in_module() {} + | -------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the comment should document the parent module use an inner doc comment + | +LL ~ //! Meant to be an +LL ~ //! inner doc comment +LL ~ //! for the module + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:27:5 + | +LL | / /// # Indented +LL | | + | |_ +LL | /// Blank line +LL | fn indented() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the documentation should include the empty line include it in the comment + | +LL | /// + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:34:1 + | +LL | / /// This should produce a warning +LL | | + | |_ +LL | fn with_doc_and_newline() {} + | ------------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:44:1 + | +LL | / /// This doc comment should produce a warning +LL | | +LL | | /** This is also a doc comment and is part of the warning +LL | | */ +LL | | + | |_ +... +LL | fn three_attributes() {} + | --------------------- the comment documents this function + | + = help: if the empty lines are unintentional remove them + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:56:5 + | +LL | / /// docs for `old_code` +LL | | // fn old_code() {} +LL | | + | |_ +LL | fn new_code() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code` comment it out + | +LL | // /// docs for `old_code` + | ++ + +error: empty lines after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:63:5 + | +LL | / /// for OldA +LL | | // struct OldA; +LL | | +LL | | /// Docs +LL | | /// for OldB +LL | | // struct OldB; +LL | | + | |_ +... +LL | struct Multiple; + | --------------- the comment documents this struct + | + = help: if the empty lines are unintentional remove them +help: if the doc comment should not document `Multiple` comment it out + | +LL ~ // /// Docs +LL ~ // /// for OldA +LL | // struct OldA; +LL | +LL ~ // /// Docs +LL ~ // /// for OldB + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:78:5 + | +LL | / /** +LL | | * Meant to be inner doc comment +LL | | */ +LL | | + | |_ +LL | fn first_in_module() {} + | -------------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the comment should document the parent module use an inner doc comment + | +LL | /*! + | ~ + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:85:5 + | +LL | / /** +LL | | * Docs for `old_code` +LL | | */ +LL | | /* fn old_code() {} */ +LL | | + | |_ +... +LL | fn new_code() {} + | ------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code` comment it out + | +LL - /** +LL + /* + | + +error: empty line after doc comment + --> tests/ui/empty_line_after/doc_comments.rs:96:5 + | +LL | / /// Docs for `old_code2` +LL | | /* fn old_code2() {} */ +LL | | + | |_ +LL | /// Docs for `new_code2` +LL | fn new_code2() {} + | -------------- the comment documents this function + | + = help: if the empty line is unintentional remove it +help: if the doc comment should not document `new_code2` comment it out + | +LL | // /// Docs for `old_code2` + | ++ + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed new file mode 100644 index 000000000000..36d80a2c95bf --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.1.fixed @@ -0,0 +1,108 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#[crate_type = "lib"] +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #[crate_type = "lib"] + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] +// This comment is isolated +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed new file mode 100644 index 000000000000..0e8e4129e858 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.2.fixed @@ -0,0 +1,111 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#![crate_type = "lib"] + +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #![crate_type = "lib"] + + + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] +// This comment is isolated +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs new file mode 100644 index 000000000000..1295088ac00e --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.rs @@ -0,0 +1,119 @@ +//@aux-build:../auxiliary/proc_macro_attr.rs +#![warn(clippy::empty_line_after_outer_attr, clippy::empty_line_after_doc_comments)] + +//~v empty_line_after_outer_attr +#[crate_type = "lib"] + +fn first_in_crate() {} + +#[macro_use] +extern crate proc_macro_attr; + +//~v empty_line_after_outer_attr +#[inline] + +/// some comment +fn with_one_newline_and_comment() {} + +#[inline] +/// some comment +fn with_no_newline_and_comment() {} + +//~v empty_line_after_outer_attr +#[inline] + +fn with_one_newline() {} + +#[rustfmt::skip] +mod two_lines { + //~v empty_line_after_outer_attr + #[crate_type = "lib"] + + + fn with_two_newlines() {} +} + +//~v empty_line_after_outer_attr +#[doc = "doc attributes should be considered attributes"] + +enum Baz { + One, + Two, +} + +//~v empty_line_after_outer_attr +#[repr(C)] + +struct Foo { + one: isize, + two: isize, +} + +//~v empty_line_after_outer_attr +#[allow(dead_code)] + +mod foo {} + +//~v empty_line_after_outer_attr +#[inline] +// Still lint cases where the empty line does not immediately follow the attribute + +fn comment_before_empty_line() {} + +//~v empty_line_after_outer_attr +#[allow(unused)] + +// This comment is isolated + +pub fn isolated_comment() {} + +#[doc = " +Returns the escaped value of the textual representation of + +"] +pub fn function() -> bool { + true +} + +#[derive(Clone, Copy)] +pub enum FooFighter { + Bar1, + + Bar2, + + Bar3, + + Bar4, +} + +#[crate_type = "lib"] +/* + +*/ +pub struct EmptyLineInBlockComment; + +#[crate_type = "lib"] +/* test */ +pub struct BlockComment; + +// See https://github.com/rust-lang/rust-clippy/issues/5567 +#[rustfmt::skip] +#[fake_async_trait] +pub trait Bazz { + fn foo() -> Vec { + let _i = ""; + + + + vec![] + } +} + +#[derive(Clone, Copy)] +#[dummy(string = "first line + +second line +")] +pub struct Args; + +fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr new file mode 100644 index 000000000000..958b40424a92 --- /dev/null +++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr @@ -0,0 +1,116 @@ +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:5:1 + | +LL | / #[crate_type = "lib"] +LL | | + | |_ +LL | fn first_in_crate() {} + | ------------------- the attribute applies to this function + | + = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` + = help: if the empty line is unintentional remove it +help: if the attribute should apply to the crate use an inner attribute + | +LL | #![crate_type = "lib"] + | + + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:13:1 + | +LL | / #[inline] +LL | | + | |_ +LL | /// some comment +LL | fn with_one_newline_and_comment() {} + | --------------------------------- the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:23:1 + | +LL | / #[inline] +LL | | + | |_ +LL | fn with_one_newline() {} + | --------------------- the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:30:5 + | +LL | / #[crate_type = "lib"] +LL | | +LL | | + | |_ +LL | fn with_two_newlines() {} + | ---------------------- the attribute applies to this function + | + = help: if the empty lines are unintentional remove them +help: if the attribute should apply to the parent module use an inner attribute + | +LL | #![crate_type = "lib"] + | + + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:37:1 + | +LL | / #[doc = "doc attributes should be considered attributes"] +LL | | + | |_ +LL | enum Baz { + | -------- the attribute applies to this enum + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:45:1 + | +LL | / #[repr(C)] +LL | | + | |_ +LL | struct Foo { + | ---------- the attribute applies to this struct + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:53:1 + | +LL | / #[allow(dead_code)] +LL | | + | |_ +LL | mod foo {} + | ------- the attribute applies to this module + | + = help: if the empty line is unintentional remove it + +error: empty line after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:58:1 + | +LL | / #[inline] +LL | | // Still lint cases where the empty line does not immediately follow the attribute +LL | | + | |_ +LL | fn comment_before_empty_line() {} + | ------------------------------ the attribute applies to this function + | + = help: if the empty line is unintentional remove it + +error: empty lines after outer attribute + --> tests/ui/empty_line_after/outer_attribute.rs:64:1 + | +LL | / #[allow(unused)] +LL | | +LL | | // This comment is isolated +LL | | + | |_ +LL | pub fn isolated_comment() {} + | ------------------------- the attribute applies to this function + | + = help: if the empty lines are unintentional remove them + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs deleted file mode 100644 index dd78491749c7..000000000000 --- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.rs +++ /dev/null @@ -1,132 +0,0 @@ -//@aux-build:proc_macro_attr.rs -#![warn(clippy::empty_line_after_doc_comments)] -#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] -#![feature(custom_inner_attributes)] -#![rustfmt::skip] - -#[macro_use] -extern crate proc_macro_attr; - -mod some_mod { - //! This doc comment should *NOT* produce a warning - - mod some_inner_mod { - fn some_noop() {} - } -} - -/// This should produce a warning - -fn with_doc_and_newline() { assert!(true)} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -/// some comment -fn with_one_newline_and_comment() { assert!(true) } - -// This should *NOT* produce a warning -#[crate_type = "lib"] -/// some comment -fn with_no_newline_and_comment() { assert!(true) } - - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -fn with_one_newline() { assert!(true) } - -// This should *NOT* produce a warning -#[crate_type = "lib"] - - -fn with_two_newlines() { assert!(true) } - - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -enum Baz { - One, - Two -} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -struct Foo { - one: isize, - two: isize -} - -// This should *NOT* produce a warning -#[crate_type = "lib"] - -mod foo { -} - -/// This doc comment should produce a warning - -/** This is also a doc comment and should produce a warning - */ - -// This should *NOT* produce a warning -#[allow(non_camel_case_types)] -#[allow(missing_docs)] -#[allow(missing_docs)] -fn three_attributes() { assert!(true) } - -// This should *NOT* produce a warning -#[doc = " -Returns the escaped value of the textual representation of - -"] -pub fn function() -> bool { - true -} - -// This should *NOT* produce a warning -#[derive(Clone, Copy)] -pub enum FooFighter { - Bar1, - - Bar2, - - Bar3, - - Bar4 -} - -// This should *NOT* produce a warning because the empty line is inside a block comment -#[crate_type = "lib"] -/* - -*/ -pub struct S; - -// This should *NOT* produce a warning -#[crate_type = "lib"] -/* test */ -pub struct T; - -// This should *NOT* produce a warning -// See https://github.com/rust-lang/rust-clippy/issues/5567 -#[fake_async_trait] -pub trait Bazz { - fn foo() -> Vec { - let _i = ""; - - - - vec![] - } -} - -#[derive(Clone, Copy)] -#[dummy(string = "first line - -second line -")] -pub struct Args; - -fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr deleted file mode 100644 index 889ccf6ba19d..000000000000 --- a/src/tools/clippy/tests/ui/empty_line_after_doc_comments.stderr +++ /dev/null @@ -1,37 +0,0 @@ -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:18:1 - | -LL | / /// This should produce a warning -LL | | -LL | | fn with_doc_and_newline() { assert!(true)} - | |_ - | - = note: `-D clippy::empty-line-after-doc-comments` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_doc_comments)]` - -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:68:1 - | -LL | / /// This doc comment should produce a warning -LL | | -LL | | /** This is also a doc comment and should produce a warning -LL | | */ -... | -LL | | #[allow(missing_docs)] -LL | | fn three_attributes() { assert!(true) } - | |_ - -error: found an empty line after a doc comment. Perhaps you need to use `//!` to make a comment on a module, remove the empty line, or make a regular comment with `//`? - --> tests/ui/empty_line_after_doc_comments.rs:70:1 - | -LL | / /** This is also a doc comment and should produce a warning -LL | | */ -LL | | -LL | | // This should *NOT* produce a warning -... | -LL | | #[allow(missing_docs)] -LL | | fn three_attributes() { assert!(true) } - | |_ - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs deleted file mode 100644 index f147cf2cd5d1..000000000000 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.rs +++ /dev/null @@ -1,120 +0,0 @@ -//@aux-build:proc_macro_attr.rs -#![warn(clippy::empty_line_after_outer_attr)] -#![allow(clippy::assertions_on_constants, clippy::duplicated_attributes)] -#![feature(custom_inner_attributes)] -#![rustfmt::skip] - -#[macro_use] -extern crate proc_macro_attr; - -// This should produce a warning -#[crate_type = "lib"] - -/// some comment -fn with_one_newline_and_comment() { assert!(true) } - -// This should not produce a warning -#[crate_type = "lib"] -/// some comment -fn with_no_newline_and_comment() { assert!(true) } - - -// This should produce a warning -#[crate_type = "lib"] - -fn with_one_newline() { assert!(true) } - -// This should produce a warning, too -#[crate_type = "lib"] - - -fn with_two_newlines() { assert!(true) } - - -// This should produce a warning -#[crate_type = "lib"] - -enum Baz { - One, - Two -} - -// This should produce a warning -#[crate_type = "lib"] - -struct Foo { - one: isize, - two: isize -} - -// This should produce a warning -#[crate_type = "lib"] - -mod foo { -} - -/// This doc comment should not produce a warning - -/** This is also a doc comment and should not produce a warning - */ - -// This should not produce a warning -#[allow(non_camel_case_types)] -#[allow(missing_docs)] -#[allow(missing_docs)] -fn three_attributes() { assert!(true) } - -// This should not produce a warning -#[doc = " -Returns the escaped value of the textual representation of - -"] -pub fn function() -> bool { - true -} - -// This should not produce a warning -#[derive(Clone, Copy)] -pub enum FooFighter { - Bar1, - - Bar2, - - Bar3, - - Bar4 -} - -// This should not produce a warning because the empty line is inside a block comment -#[crate_type = "lib"] -/* - -*/ -pub struct S; - -// This should not produce a warning -#[crate_type = "lib"] -/* test */ -pub struct T; - -// This should not produce a warning -// See https://github.com/rust-lang/rust-clippy/issues/5567 -#[fake_async_trait] -pub trait Bazz { - fn foo() -> Vec { - let _i = ""; - - - - vec![] - } -} - -#[derive(Clone, Copy)] -#[dummy(string = "first line - -second line -")] -pub struct Args; - -fn main() {} diff --git a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr deleted file mode 100644 index b43e6e30da22..000000000000 --- a/src/tools/clippy/tests/ui/empty_line_after_outer_attribute.stderr +++ /dev/null @@ -1,54 +0,0 @@ -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:11:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | /// some comment -LL | | fn with_one_newline_and_comment() { assert!(true) } - | |_ - | - = note: `-D clippy::empty-line-after-outer-attr` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::empty_line_after_outer_attr)]` - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:23:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | fn with_one_newline() { assert!(true) } - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:28:1 - | -LL | / #[crate_type = "lib"] -... | -LL | | fn with_two_newlines() { assert!(true) } - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:35:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | enum Baz { - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:43:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | struct Foo { - | |_ - -error: found an empty line after an outer attribute. Perhaps you forgot to add a `!` to make it an inner attribute? - --> tests/ui/empty_line_after_outer_attribute.rs:51:1 - | -LL | / #[crate_type = "lib"] -LL | | -LL | | mod foo { - | |_ - -error: aborting due to 6 previous errors - diff --git a/src/tools/clippy/tests/ui/empty_loop_no_std.rs b/src/tools/clippy/tests/ui/empty_loop_no_std.rs index 5fe32351ed45..1bb895bda75d 100644 --- a/src/tools/clippy/tests/ui/empty_loop_no_std.rs +++ b/src/tools/clippy/tests/ui/empty_loop_no_std.rs @@ -1,5 +1,5 @@ //@compile-flags: -Clink-arg=-nostartfiles -//@ignore-target-apple +//@ignore-target: apple #![warn(clippy::empty_loop)] #![feature(lang_items, start, libc)] diff --git a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs index c50404c5047b..37849179d6a0 100644 --- a/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs +++ b/src/tools/clippy/tests/ui/enum_clike_unportable_variant.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::enum_clike_unportable_variant)] #![allow(unused, non_upper_case_globals)] diff --git a/src/tools/clippy/tests/ui/exit1.rs b/src/tools/clippy/tests/ui/exit1.rs index a89f6dd4ca0e..36b3c42fd995 100644 --- a/src/tools/clippy/tests/ui/exit1.rs +++ b/src/tools/clippy/tests/ui/exit1.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn not_main() { if true { diff --git a/src/tools/clippy/tests/ui/exit2.rs b/src/tools/clippy/tests/ui/exit2.rs index d5ff93fb9ccb..9bbb7b073a4b 100644 --- a/src/tools/clippy/tests/ui/exit2.rs +++ b/src/tools/clippy/tests/ui/exit2.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn also_not_main() { std::process::exit(3); diff --git a/src/tools/clippy/tests/ui/exit3.rs b/src/tools/clippy/tests/ui/exit3.rs index 9dc0e1015a4f..cab908aafd33 100644 --- a/src/tools/clippy/tests/ui/exit3.rs +++ b/src/tools/clippy/tests/ui/exit3.rs @@ -1,4 +1,4 @@ -#[warn(clippy::exit)] +#![warn(clippy::exit)] fn main() { if true { diff --git a/src/tools/clippy/tests/ui/expect_fun_call.fixed b/src/tools/clippy/tests/ui/expect_fun_call.fixed index 6ac3c43ad298..8f800c71941a 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.fixed +++ b/src/tools/clippy/tests/ui/expect_fun_call.fixed @@ -5,8 +5,6 @@ clippy::unnecessary_literal_unwrap )] -/// Checks implementation of the `EXPECT_FUN_CALL` lint - macro_rules! one { () => { 1 diff --git a/src/tools/clippy/tests/ui/expect_fun_call.rs b/src/tools/clippy/tests/ui/expect_fun_call.rs index 22ea2db504fa..b5cfafb2993e 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.rs +++ b/src/tools/clippy/tests/ui/expect_fun_call.rs @@ -5,8 +5,6 @@ clippy::unnecessary_literal_unwrap )] -/// Checks implementation of the `EXPECT_FUN_CALL` lint - macro_rules! one { () => { 1 diff --git a/src/tools/clippy/tests/ui/expect_fun_call.stderr b/src/tools/clippy/tests/ui/expect_fun_call.stderr index b41904d04fa4..bae853ac5c19 100644 --- a/src/tools/clippy/tests/ui/expect_fun_call.stderr +++ b/src/tools/clippy/tests/ui/expect_fun_call.stderr @@ -1,5 +1,5 @@ error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:37:26 + --> tests/ui/expect_fun_call.rs:35:26 | LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` @@ -8,85 +8,85 @@ LL | with_none_and_format.expect(&format!("Error {}: fake error", error_code = help: to override `-D warnings` add `#[allow(clippy::expect_fun_call)]` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:40:26 + --> tests/ui/expect_fun_call.rs:38:26 | LL | with_none_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:43:37 + --> tests/ui/expect_fun_call.rs:41:37 | LL | with_none_and_format_with_macro.expect(format!("Error {}: fake error", one!()).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("Error {}: fake error", one!()))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:53:25 + --> tests/ui/expect_fun_call.rs:51:25 | LL | with_err_and_format.expect(&format!("Error {}: fake error", error_code)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:56:25 + --> tests/ui/expect_fun_call.rs:54:25 | LL | with_err_and_as_str.expect(format!("Error {}: fake error", error_code).as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|_| panic!("Error {}: fake error", error_code))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:68:17 + --> tests/ui/expect_fun_call.rs:66:17 | LL | Some("foo").expect(format!("{} {}", 1, 2).as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{} {}", 1, 2))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:89:21 + --> tests/ui/expect_fun_call.rs:87:21 | LL | Some("foo").expect(&get_string()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:90:21 + --> tests/ui/expect_fun_call.rs:88:21 | LL | Some("foo").expect(get_string().as_ref()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:91:21 + --> tests/ui/expect_fun_call.rs:89:21 | LL | Some("foo").expect(get_string().as_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:93:21 + --> tests/ui/expect_fun_call.rs:91:21 | LL | Some("foo").expect(get_static_str()); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_static_str()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:94:21 + --> tests/ui/expect_fun_call.rs:92:21 | LL | Some("foo").expect(get_non_static_str(&0)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| { panic!("{}", get_non_static_str(&0).to_string()) })` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:98:16 + --> tests/ui/expect_fun_call.rs:96:16 | LL | Some(true).expect(&format!("key {}, {}", 1, 2)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("key {}, {}", 1, 2))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:104:17 + --> tests/ui/expect_fun_call.rs:102:17 | LL | opt_ref.expect(&format!("{:?}", opt_ref)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{:?}", opt_ref))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:108:20 + --> tests/ui/expect_fun_call.rs:106:20 | LL | format_capture.expect(&format!("{error_code}")); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}"))` error: use of `expect` followed by a function call - --> tests/ui/expect_fun_call.rs:111:30 + --> tests/ui/expect_fun_call.rs:109:30 | LL | format_capture_and_value.expect(&format!("{error_code}, {}", 1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| panic!("{error_code}, {}", 1))` diff --git a/src/tools/clippy/tests/ui/field_reassign_with_default.rs b/src/tools/clippy/tests/ui/field_reassign_with_default.rs index 620cffb4f04d..2a432751952d 100644 --- a/src/tools/clippy/tests/ui/field_reassign_with_default.rs +++ b/src/tools/clippy/tests/ui/field_reassign_with_default.rs @@ -192,8 +192,8 @@ struct WrapperMulti { } mod issue6312 { - use std::sync::atomic::AtomicBool; use std::sync::Arc; + use std::sync::atomic::AtomicBool; // do not lint: type implements `Drop` but not all fields are `Copy` #[derive(Clone, Default)] diff --git a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs index 47c113d61c04..8ea75fae89b6 100644 --- a/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs +++ b/src/tools/clippy/tests/ui/floating_point_arithmetic_nostd.rs @@ -4,7 +4,7 @@ #![no_std] // The following should not lint, as the suggested methods `{f16,f32,f64,f128}.mul_add()` -// and ``{f16,f32,f64,f128}::abs()` are not available in no_std +// and `{f16,f32,f64,f128}::abs()` are not available in no_std pub fn mul_add() { let a: f64 = 1234.567; diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed index 4d812f6bf1dc..20d1e3d90504 100644 --- a/src/tools/clippy/tests/ui/format_args.fixed +++ b/src/tools/clippy/tests/ui/format_args.fixed @@ -8,7 +8,7 @@ clippy::uninlined_format_args )] -use std::io::{stdout, Write}; +use std::io::{Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs index d242623feb62..18ab223db78a 100644 --- a/src/tools/clippy/tests/ui/format_args.rs +++ b/src/tools/clippy/tests/ui/format_args.rs @@ -8,7 +8,7 @@ clippy::uninlined_format_args )] -use std::io::{stdout, Write}; +use std::io::{Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs index b7492e38b256..f04715f4f018 100644 --- a/src/tools/clippy/tests/ui/format_args_unfixable.rs +++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs @@ -2,7 +2,7 @@ #![allow(unused)] #![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)] -use std::io::{stdout, Error, ErrorKind, Write}; +use std::io::{Error, ErrorKind, Write, stdout}; use std::ops::Deref; use std::panic::Location; diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed index ad13372a68b7..1f47dddcbc40 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed @@ -113,6 +113,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { Ok(()) } +fn issue13407(s: &str) -> Option { + (s == "1").then(|| true) +} + const fn issue12103(x: u32) -> Option { // Should not issue an error in `const` context if x > 42 { Some(150) } else { None } diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs index 73edbb7da2a8..499f008fb876 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs @@ -131,6 +131,10 @@ fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> { Ok(()) } +fn issue13407(s: &str) -> Option { + if s == "1" { Some(true) } else { None } +} + const fn issue12103(x: u32) -> Option { // Should not issue an error in `const` context if x > 42 { Some(150) } else { None } diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr index aed01e026cbe..e7bc66b3ee88 100644 --- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr +++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr @@ -52,5 +52,11 @@ LL | | None LL | | }; | |_____^ help: try: `foo().then(|| { println!("true!"); 150 })` -error: aborting due to 5 previous errors +error: this could be simplified with `bool::then` + --> tests/ui/if_then_some_else_none.rs:135:5 + | +LL | if s == "1" { Some(true) } else { None } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(s == "1").then(|| true)` + +error: aborting due to 6 previous errors diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed index 118f0b488952..fde404373098 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed @@ -21,15 +21,12 @@ fn main() { let _ = foo().map_err(|()| todo!()); //~^ ERROR: matching over `()` is more explicit - println!( - "{:?}", - match foo() { - Ok(()) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(()) => {}, - //~^ ERROR: matching over `()` is more explicit - } - ); + println!("{:?}", match foo() { + Ok(()) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(()) => {}, + //~^ ERROR: matching over `()` is more explicit + }); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs index 92feb9e6c281..528844d76e05 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs @@ -21,15 +21,12 @@ fn main() { let _ = foo().map_err(|_| todo!()); //~^ ERROR: matching over `()` is more explicit - println!( - "{:?}", - match foo() { - Ok(_) => {}, - //~^ ERROR: matching over `()` is more explicit - Err(_) => {}, - //~^ ERROR: matching over `()` is more explicit - } - ); + println!("{:?}", match foo() { + Ok(_) => {}, + //~^ ERROR: matching over `()` is more explicit + Err(_) => {}, + //~^ ERROR: matching over `()` is more explicit + }); } // ignored_unit_patterns in derive macro should be ok diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr index 00a254e39192..54ff4454d6bd 100644 --- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr +++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr @@ -26,31 +26,31 @@ LL | let _ = foo().map_err(|_| todo!()); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:27:16 + --> tests/ui/ignored_unit_patterns.rs:25:12 | -LL | Ok(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Ok(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:29:17 + --> tests/ui/ignored_unit_patterns.rs:27:13 | -LL | Err(_) => {}, - | ^ help: use `()` instead of `_`: `()` +LL | Err(_) => {}, + | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:41:9 + --> tests/ui/ignored_unit_patterns.rs:38:9 | LL | let _ = foo().unwrap(); | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:50:13 + --> tests/ui/ignored_unit_patterns.rs:47:13 | LL | (1, _) => unimplemented!(), | ^ help: use `()` instead of `_`: `()` error: matching over `()` is more explicit - --> tests/ui/ignored_unit_patterns.rs:57:13 + --> tests/ui/ignored_unit_patterns.rs:54:13 | LL | for (x, _) in v { | ^ help: use `()` instead of `_`: `()` diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed index 27f679797dd9..81cc14949144 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.fixed @@ -184,18 +184,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { @@ -214,4 +214,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs index 5d7b95d2c652..f73396ebd27e 100644 --- a/src/tools/clippy/tests/ui/implicit_saturating_sub.rs +++ b/src/tools/clippy/tests/ui/implicit_saturating_sub.rs @@ -230,18 +230,18 @@ fn main() { let mut m = Mock; let mut u_32 = 3000; let a = 200; - let mut _b = 8; + let mut b = 8; if m != 0 { m -= 1; } if a > 0 { - _b -= 1; + b -= 1; } if 0 > a { - _b -= 1; + b -= 1; } if u_32 > 0 { @@ -260,4 +260,11 @@ fn main() { } else if u_32 > 0 { u_32 -= 1; } + + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; } diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs index 8ef1f554bc35..c23df223d508 100644 --- a/src/tools/clippy/tests/ui/incompatible_msrv.rs +++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs @@ -2,8 +2,8 @@ #![feature(custom_inner_attributes)] #![clippy::msrv = "1.3.0"] -use std::collections::hash_map::Entry; use std::collections::HashMap; +use std::collections::hash_map::Entry; use std::future::Future; use std::thread::sleep; use std::time::Duration; diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed index a6ef8f8c119a..3a2294ef4c59 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.fixed @@ -22,8 +22,8 @@ macro_rules! b { }; } -use std::u32::MAX; use std::u8::MIN; +use std::u32::MAX; use std::{f64, u32}; #[warn(clippy::legacy_numeric_constants)] @@ -99,8 +99,8 @@ fn allow() { ::std::primitive::u8::MIN; ::std::u8::MIN; ::std::primitive::u8::min_value(); - use std::u64; use std::u8::MIN; + use std::u64; } #[warn(clippy::legacy_numeric_constants)] diff --git a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs index cd633545372c..6cb3e694ea14 100644 --- a/src/tools/clippy/tests/ui/legacy_numeric_constants.rs +++ b/src/tools/clippy/tests/ui/legacy_numeric_constants.rs @@ -22,8 +22,8 @@ macro_rules! b { }; } -use std::u32::MAX; use std::u8::MIN; +use std::u32::MAX; use std::{f64, u32}; #[warn(clippy::legacy_numeric_constants)] @@ -99,8 +99,8 @@ fn allow() { ::std::primitive::u8::MIN; ::std::u8::MIN; ::std::primitive::u8::min_value(); - use std::u64; use std::u8::MIN; + use std::u64; } #[warn(clippy::legacy_numeric_constants)] diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed index 38ed5a957e73..28844fab7479 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.fixed +++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs index ae6cc16ed276..5381f2959898 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports.rs +++ b/src/tools/clippy/tests/ui/macro_use_imports.rs @@ -2,7 +2,7 @@ //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs index df6d5b9fbabf..60cce1d24a28 100644 --- a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs +++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs //@aux-build:macro_use_helper.rs //@aux-build:proc_macro_derive.rs -//@ignore-32bit +//@ignore-bitwidth: 32 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)] #![allow(clippy::single_component_path_imports)] diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs new file mode 100644 index 000000000000..e97e3bdfef76 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.rs @@ -0,0 +1,35 @@ +//@no-rustfix +#![warn(clippy::implicit_saturating_sub)] +#![allow(arithmetic_overflow)] + +fn main() { + let a = 12u32; + let b = 13u32; + + let result = if a > b { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { b - a } else { 0 }; + //~^ ERROR: inverted arithmetic check before subtraction + + let result = if a > b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if a >= b { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b < a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + let result = if b <= a { 0 } else { a - b }; + //~^ ERROR: inverted arithmetic check before subtraction + + let af = 12f32; + let bf = 13f32; + // Should not lint! + let result = if bf < af { 0. } else { af - bf }; + + // Should not lint! + let result = if a < b { + println!("we shouldn't remove this"); + 0 + } else { + a - b + }; +} diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr new file mode 100644 index 000000000000..4121aa7464f0 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check-2.stderr @@ -0,0 +1,75 @@ +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:9:23 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:9:29 + | +LL | let result = if a > b { b - a } else { 0 }; + | ^^^^^ + = note: `#[deny(clippy::inverted_saturating_sub)]` on by default + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:11:23 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^ ----- help: try replacing it with: `a - b` + | +note: this subtraction underflows when `b < a` + --> tests/ui/manual_arithmetic_check-2.rs:11:29 + | +LL | let result = if b < a { b - a } else { 0 }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:14:23 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:14:40 + | +LL | let result = if a > b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:16:23 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:16:41 + | +LL | let result = if a >= b { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:18:23 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:18:40 + | +LL | let result = if b < a { 0 } else { a - b }; + | ^^^^^ + +error: inverted arithmetic check before subtraction + --> tests/ui/manual_arithmetic_check-2.rs:20:23 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^ ----- help: try replacing it with: `b - a` + | +note: this subtraction underflows when `a < b` + --> tests/ui/manual_arithmetic_check-2.rs:20:41 + | +LL | let result = if b <= a { 0 } else { a - b }; + | ^^^^^ + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed b/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed new file mode 100644 index 000000000000..29ecbb9ad2ad --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.fixed @@ -0,0 +1,24 @@ +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] +#![allow(clippy::if_same_then_else)] + +fn main() { + let a = 12u32; + let b = 13u32; + let c = 8u32; + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + let result = a.saturating_sub(b); + //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; +} diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.rs b/src/tools/clippy/tests/ui/manual_arithmetic_check.rs new file mode 100644 index 000000000000..69554c6b61ca --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.rs @@ -0,0 +1,24 @@ +#![warn(clippy::implicit_saturating_sub, clippy::inverted_saturating_sub)] +#![allow(clippy::if_same_then_else)] + +fn main() { + let a = 12u32; + let b = 13u32; + let c = 8u32; + + let result = if a > b { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + let result = if b < a { a - b } else { 0 }; + //~^ ERROR: manual arithmetic check found + + let result = if a < b { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + let result = if b > a { 0 } else { a - b }; + //~^ ERROR: manual arithmetic check found + + // Should not warn! + let result = if a > b { a - b } else { a - c }; + + // Just to check it won't break clippy. + let result = if b > a { 0 } else { 0 }; +} diff --git a/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr b/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr new file mode 100644 index 000000000000..b0cf73cd9151 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_arithmetic_check.stderr @@ -0,0 +1,29 @@ +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:9:18 + | +LL | let result = if a > b { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + | + = note: `-D clippy::implicit-saturating-sub` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implicit_saturating_sub)]` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:11:18 + | +LL | let result = if b < a { a - b } else { 0 }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:14:18 + | +LL | let result = if a < b { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: manual arithmetic check found + --> tests/ui/manual_arithmetic_check.rs:16:18 + | +LL | let result = if b > a { 0 } else { a - b }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a.saturating_sub(b)` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.fixed b/src/tools/clippy/tests/ui/manual_div_ceil.fixed new file mode 100644 index 000000000000..e7801f7376aa --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil.fixed @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + let _ = x.div_ceil(y); //~ ERROR: manually reimplementing `div_ceil` + + let _ = 7_u32.div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32).div_ceil(4); //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.rs b/src/tools/clippy/tests/ui/manual_div_ceil.rs new file mode 100644 index 000000000000..2de74c7eaa88 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil.rs @@ -0,0 +1,30 @@ +#![warn(clippy::manual_div_ceil)] + +fn main() { + let x = 7_u32; + let y = 4_u32; + let z = 11_u32; + + // Lint + let _ = (x + (y - 1)) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = ((y - 1) + x) / y; //~ ERROR: manually reimplementing `div_ceil` + let _ = (x + y - 1) / y; //~ ERROR: manually reimplementing `div_ceil` + + let _ = (7_u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + let _ = (7_i32 as u32 + (4 - 1)) / 4; //~ ERROR: manually reimplementing `div_ceil` + + // No lint + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; + + let x_i = 7_i32; + let y_i = 4_i32; + let z_i = 11_i32; + + // No lint because `int_roundings` feature is not enabled. + let _ = (z as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (y_i - 1)) / y_i; + let _ = (7_u32 as i32 + (4 - 1)) / 4; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil.stderr b/src/tools/clippy/tests/ui/manual_div_ceil.stderr new file mode 100644 index 000000000000..dc652dff405f --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil.stderr @@ -0,0 +1,35 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:9:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:10:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:11:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:13:13 + | +LL | let _ = (7_u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_u32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil.rs:14:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed new file mode 100644 index 000000000000..a1d678c66898 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.fixed @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + let _ = x.div_ceil(y); + + let _ = 7_i32.div_ceil(4); + let _ = (7_i32 as u32).div_ceil(4); + let _ = (7_u32 as i32).div_ceil(4); + let _ = z_u.div_ceil(4); + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs new file mode 100644 index 000000000000..58cb1dbe34d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.rs @@ -0,0 +1,25 @@ +#![warn(clippy::manual_div_ceil)] +#![feature(int_roundings)] + +fn main() { + let x = 7_i32; + let y = 4_i32; + let z = 3_i32; + let z_u: u32 = 11; + + // Lint. + let _ = (x + (y - 1)) / y; + let _ = ((y - 1) + x) / y; + let _ = (x + y - 1) / y; + + let _ = (7_i32 + (4 - 1)) / 4; + let _ = (7_i32 as u32 + (4 - 1)) / 4; + let _ = (7_u32 as i32 + (4 - 1)) / 4; + let _ = (z_u + (4 - 1)) / 4; + + // No lint. + let _ = (x + (y - 2)) / y; + let _ = (x + (y + 1)) / y; + + let _ = (x + (y - 1)) / z; +} diff --git a/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr new file mode 100644 index 000000000000..361ef9bd9f42 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_div_ceil_with_feature.stderr @@ -0,0 +1,47 @@ +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:11:13 + | +LL | let _ = (x + (y - 1)) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + | + = note: `-D clippy::manual-div-ceil` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_div_ceil)]` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:12:13 + | +LL | let _ = ((y - 1) + x) / y; + | ^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:13:13 + | +LL | let _ = (x + y - 1) / y; + | ^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `x.div_ceil(y)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:15:13 + | +LL | let _ = (7_i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `7_i32.div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:16:13 + | +LL | let _ = (7_i32 as u32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_i32 as u32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:17:13 + | +LL | let _ = (7_u32 as i32 + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `(7_u32 as i32).div_ceil(4)` + +error: manually reimplementing `div_ceil` + --> tests/ui/manual_div_ceil_with_feature.rs:18:13 + | +LL | let _ = (z_u + (4 - 1)) / 4; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.div_ceil()`: `z_u.div_ceil(4)` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed new file mode 100644 index 000000000000..dda5fe0ec3e7 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.fixed @@ -0,0 +1,20 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + + // Test different orders of expression + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + let _ = a.is_power_of_two(); + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.rs b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs new file mode 100644 index 000000000000..a1d3a95c4102 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.rs @@ -0,0 +1,20 @@ +#![warn(clippy::manual_is_power_of_two)] + +fn main() { + let a = 16_u64; + + let _ = a.count_ones() == 1; + let _ = a & (a - 1) == 0; + + // Test different orders of expression + let _ = 1 == a.count_ones(); + let _ = (a - 1) & a == 0; + let _ = 0 == a & (a - 1); + let _ = 0 == (a - 1) & a; + + let b = 4_i64; + + // is_power_of_two only works for unsigned integers + let _ = b.count_ones() == 1; + let _ = b & (b - 1) == 0; +} diff --git a/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr new file mode 100644 index 000000000000..3cfc6297abf2 --- /dev/null +++ b/src/tools/clippy/tests/ui/manual_is_power_of_two.stderr @@ -0,0 +1,41 @@ +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:6:13 + | +LL | let _ = a.count_ones() == 1; + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + | + = note: `-D clippy::manual-is-power-of-two` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::manual_is_power_of_two)]` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:7:13 + | +LL | let _ = a & (a - 1) == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:10:13 + | +LL | let _ = 1 == a.count_ones(); + | ^^^^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:11:13 + | +LL | let _ = (a - 1) & a == 0; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:12:13 + | +LL | let _ = 0 == a & (a - 1); + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: manually reimplementing `is_power_of_two` + --> tests/ui/manual_is_power_of_two.rs:13:13 + | +LL | let _ = 0 == (a - 1) & a; + | ^^^^^^^^^^^^^^^^ help: consider using `.is_power_of_two()`: `a.is_power_of_two()` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs index 31c3cc801372..ffe2bb924673 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs @@ -1,8 +1,8 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -enum E { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub enum E { + //~^ manual_non_exhaustive A, B, #[doc(hidden)] @@ -11,7 +11,7 @@ enum E { // if the user explicitly marks as nonexhaustive we shouldn't warn them #[non_exhaustive] -enum Ep { +pub enum Ep { A, B, #[doc(hidden)] @@ -19,14 +19,14 @@ enum Ep { } // marker variant does not have doc hidden attribute, should be ignored -enum NoDocHidden { +pub enum NoDocHidden { A, B, _C, } // name of variant with doc hidden does not start with underscore -enum NoUnderscore { +pub enum NoUnderscore { A, B, #[doc(hidden)] @@ -34,7 +34,7 @@ enum NoUnderscore { } // variant with doc hidden is not unit, should be ignored -enum NotUnit { +pub enum NotUnit { A, B, #[doc(hidden)] @@ -42,13 +42,13 @@ enum NotUnit { } // variant with doc hidden is the only one, should be ignored -enum OnlyMarker { +pub enum OnlyMarker { #[doc(hidden)] _A, } // variant with multiple markers, should be ignored -enum MultipleMarkers { +pub enum MultipleMarkers { A, #[doc(hidden)] _B, @@ -58,13 +58,13 @@ enum MultipleMarkers { // already non_exhaustive and no markers, should be ignored #[non_exhaustive] -enum NonExhaustive { +pub enum NonExhaustive { A, B, } // marked is used, don't lint -enum UsedHidden { +pub enum UsedHidden { #[doc(hidden)] _A, B, @@ -77,11 +77,9 @@ fn foo(x: &mut UsedHidden) { } #[expect(clippy::manual_non_exhaustive)] -enum ExpectLint { +pub enum ExpectLint { A, B, #[doc(hidden)] _C, } - -fn main() {} diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr index dc669568dd2d..0a9ac157f850 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:4:1 | -LL | enum E { - | ^----- - | | - | _help: add the attribute: `#[non_exhaustive] enum E` - | | +LL | / pub enum E { LL | | LL | | A, LL | | B, @@ -21,15 +17,16 @@ LL | _C, | ^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum E { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_enum.rs:29:1 | -LL | enum NoUnderscore { - | ^---------------- - | | - | _help: add the attribute: `#[non_exhaustive] enum NoUnderscore` - | | +LL | / pub enum NoUnderscore { LL | | A, LL | | B, LL | | #[doc(hidden)] @@ -42,6 +39,11 @@ help: remove this variant | LL | C, | ^ +help: use the `#[non_exhaustive]` attribute instead + | +LL + #[non_exhaustive] +LL | pub enum NoUnderscore { + | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs index 4b2803ccc4a7..31dd50281fa1 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.rs @@ -1,9 +1,9 @@ #![warn(clippy::manual_non_exhaustive)] #![allow(unused)] //@no-rustfix -mod structs { - struct S { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod structs { + pub struct S { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), @@ -11,68 +11,76 @@ mod structs { // user forgot to remove the private field #[non_exhaustive] - struct Sp { - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Sp { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, _c: (), } // some other fields are private, should be ignored - struct PrivateFields { + pub struct PrivateFields { a: i32, pub b: i32, _c: (), } - // private field name does not start with underscore, should be ignored - struct NoUnderscore { + pub struct NoUnderscore { + //~^ manual_non_exhaustive pub a: i32, pub b: i32, c: (), } // private field is not unit type, should be ignored - struct NotUnit { + pub struct NotUnit { pub a: i32, pub b: i32, _c: i32, } // private field is the only field, should be ignored - struct OnlyMarker { + pub struct OnlyMarker { _a: (), } // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive { + pub struct NonExhaustive { pub a: i32, pub b: i32, } } -mod tuple_structs { - struct T(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern +pub mod tuple_structs { + pub struct T(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // user forgot to remove the private field #[non_exhaustive] - struct Tp(pub i32, pub i32, ()); - //~^ ERROR: this seems like a manual implementation of the non-exhaustive pattern + pub struct Tp(pub i32, pub i32, ()); + //~^ manual_non_exhaustive // some other fields are private, should be ignored - struct PrivateFields(pub i32, i32, ()); + pub struct PrivateFields(pub i32, i32, ()); // private field is not unit type, should be ignored - struct NotUnit(pub i32, pub i32, i32); + pub struct NotUnit(pub i32, pub i32, i32); // private field is the only field, should be ignored - struct OnlyMarker(()); + pub struct OnlyMarker(()); // already non exhaustive and no private fields, should be ignored #[non_exhaustive] - struct NonExhaustive(pub i32, pub i32); + pub struct NonExhaustive(pub i32, pub i32); } -fn main() {} +mod private { + // Don't lint structs that are not actually public as `#[non_exhaustive]` only applies to + // external crates. The manual pattern can still be used to get module local non exhaustiveness + pub struct NotPublic { + pub a: i32, + pub b: i32, + _c: (), + } +} diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr index 1cab812988a3..81de1e4f2719 100644 --- a/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr +++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_struct.stderr @@ -1,11 +1,7 @@ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:5:5 | -LL | struct S { - | ^------- - | | - | _____help: add the attribute: `#[non_exhaustive] struct S` - | | +LL | / pub struct S { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -20,11 +16,16 @@ LL | _c: (), | ^^^^^^ = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::manual_non_exhaustive)]` +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct S { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:14:5 | -LL | / struct Sp { +LL | / pub struct Sp { LL | | LL | | pub a: i32, LL | | pub b: i32, @@ -32,6 +33,11 @@ LL | | _c: (), LL | | } | |_____^ | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:13:5 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field --> tests/ui/manual_non_exhaustive_struct.rs:18:9 | @@ -39,13 +45,10 @@ LL | _c: (), | ^^^^^^ error: this seems like a manual implementation of the non-exhaustive pattern - --> tests/ui/manual_non_exhaustive_struct.rs:29:5 + --> tests/ui/manual_non_exhaustive_struct.rs:28:5 | -LL | struct NoUnderscore { - | ^------------------ - | | - | _____help: add the attribute: `#[non_exhaustive] struct NoUnderscore` - | | +LL | / pub struct NoUnderscore { +LL | | LL | | pub a: i32, LL | | pub b: i32, LL | | c: (), @@ -57,32 +60,45 @@ help: remove this field | LL | c: (), | ^^^^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct NoUnderscore { + | error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:56:5 | -LL | struct T(pub i32, pub i32, ()); - | --------^^^^^^^^^^^^^^^^^^^^^^^ - | | - | help: add the attribute: `#[non_exhaustive] struct T` +LL | pub struct T(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:56:32 + --> tests/ui/manual_non_exhaustive_struct.rs:56:36 + | +LL | pub struct T(pub i32, pub i32, ()); + | ^^ +help: use the `#[non_exhaustive]` attribute instead + | +LL ~ #[non_exhaustive] +LL ~ pub struct T(pub i32, pub i32, ()); | -LL | struct T(pub i32, pub i32, ()); - | ^^ error: this seems like a manual implementation of the non-exhaustive pattern --> tests/ui/manual_non_exhaustive_struct.rs:61:5 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | +note: the struct is already non-exhaustive + --> tests/ui/manual_non_exhaustive_struct.rs:60:5 + | +LL | #[non_exhaustive] + | ^^^^^^^^^^^^^^^^^ help: remove this field - --> tests/ui/manual_non_exhaustive_struct.rs:61:33 + --> tests/ui/manual_non_exhaustive_struct.rs:61:37 | -LL | struct Tp(pub i32, pub i32, ()); - | ^^ +LL | pub struct Tp(pub i32, pub i32, ()); + | ^^ error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.fixed b/src/tools/clippy/tests/ui/manual_range_patterns.fixed index f1b99637afdb..60467bf9e884 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.fixed +++ b/src/tools/clippy/tests/ui/manual_range_patterns.fixed @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } diff --git a/src/tools/clippy/tests/ui/manual_range_patterns.rs b/src/tools/clippy/tests/ui/manual_range_patterns.rs index 869ffbe80b97..9cd803334499 100644 --- a/src/tools/clippy/tests/ui/manual_range_patterns.rs +++ b/src/tools/clippy/tests/ui/manual_range_patterns.rs @@ -45,4 +45,12 @@ fn main() { }; } mac!(f); + + #[rustfmt::skip] + let _ = match f { + | 2..=15 => 4, + | 241..=254 => 5, + | 255 => 6, + | _ => 7, + }; } diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed index 41a32df32ee4..528a65790c4c 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.fixed @@ -1,6 +1,6 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i128, i32, u128, u32}; +use std::{i32, i128, u32, u128}; fn main() { let _ = 1u32.saturating_add(1); diff --git a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs index 3a6b32d80690..55f3720dfcc0 100644 --- a/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs +++ b/src/tools/clippy/tests/ui/manual_saturating_arithmetic.rs @@ -1,6 +1,6 @@ #![allow(clippy::legacy_numeric_constants, unused_imports)] -use std::{i128, i32, u128, u32}; +use std::{i32, i128, u32, u128}; fn main() { let _ = 1u32.checked_add(1).unwrap_or(u32::max_value()); diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs index 4457ae73da2f..a056fdeaa5d0 100644 --- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs @@ -2,8 +2,6 @@ #![allow(clippy::redundant_pattern_matching)] #![allow(clippy::if_same_then_else, clippy::equatable_if_let, clippy::needless_if)] -/// Tests for match_overlapping_arm - fn overlapping() { const FOO: u64 = 2; diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr index 65092ffbb555..a60a09a07990 100644 --- a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr +++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr @@ -1,11 +1,11 @@ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:11:9 + --> tests/ui/match_overlapping_arm.rs:9:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:13:9 + --> tests/ui/match_overlapping_arm.rs:11:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ @@ -13,85 +13,85 @@ LL | 0..=11 => println!("0..=11"), = help: to override `-D warnings` add `#[allow(clippy::match_overlapping_arm)]` error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:18:9 + --> tests/ui/match_overlapping_arm.rs:16:9 | LL | 0..=5 => println!("0..=5"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:21:9 + --> tests/ui/match_overlapping_arm.rs:19:9 | LL | FOO..=11 => println!("FOO..=11"), | ^^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:56:9 + --> tests/ui/match_overlapping_arm.rs:54:9 | LL | 0..11 => println!("0..11"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:58:9 + --> tests/ui/match_overlapping_arm.rs:56:9 | LL | 0..=11 => println!("0..=11"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:82:9 + --> tests/ui/match_overlapping_arm.rs:80:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:81:9 + --> tests/ui/match_overlapping_arm.rs:79:9 | LL | 5..14 => println!("5..14"), | ^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:88:9 + --> tests/ui/match_overlapping_arm.rs:86:9 | LL | 0..7 => println!("0..7"), | ^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:90:9 + --> tests/ui/match_overlapping_arm.rs:88:9 | LL | 0..=10 => println!("0..=10"), | ^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:101:9 + --> tests/ui/match_overlapping_arm.rs:99:9 | LL | ..=23 => println!("..=23"), | ^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:103:9 + --> tests/ui/match_overlapping_arm.rs:101:9 | LL | ..26 => println!("..26"), | ^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:111:9 + --> tests/ui/match_overlapping_arm.rs:109:9 | LL | 21..=30 => (), | ^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:113:9 + --> tests/ui/match_overlapping_arm.rs:111:9 | LL | 21..=40 => (), | ^^^^^^^ error: some ranges overlap - --> tests/ui/match_overlapping_arm.rs:126:9 + --> tests/ui/match_overlapping_arm.rs:124:9 | LL | 0..=0x0000_0000_0000_00ff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ | note: overlaps with this - --> tests/ui/match_overlapping_arm.rs:128:9 + --> tests/ui/match_overlapping_arm.rs:126:9 | LL | 0..=0x0000_0000_0000_ffff => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.rs b/src/tools/clippy/tests/ui/mixed_attributes_style.rs index 1a646c265223..93ab9392cf8b 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.rs +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.rs @@ -82,7 +82,8 @@ mod issue_12530 { #![allow(dead_code)] } } - /// Nested mod //~ ERROR: item has both inner and outer attributes + /// Nested mod + //~^ ERROR: item has both inner and outer attributes #[allow(unused)] mod nest_mod_2 { #![allow(unused)] diff --git a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr index a1d3fc430f6c..abdc2df23cf1 100644 --- a/src/tools/clippy/tests/ui/mixed_attributes_style.stderr +++ b/src/tools/clippy/tests/ui/mixed_attributes_style.stderr @@ -46,13 +46,14 @@ error: item has both inner and outer attributes --> tests/ui/mixed_attributes_style.rs:85:5 | LL | / /// Nested mod +LL | | LL | | #[allow(unused)] LL | | mod nest_mod_2 { LL | | #![allow(unused)] | |_________________________^ error: item has both inner and outer attributes - --> tests/ui/mixed_attributes_style.rs:90:9 + --> tests/ui/mixed_attributes_style.rs:91:9 | LL | / #[allow(dead_code)] LL | | mod inner_mod { diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index adb266ffbc8d..2459af606884 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -7,8 +7,8 @@ )] #![warn(clippy::must_use_candidate)] use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; pub struct MyAtomic(AtomicBool); pub struct MyPure; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 49bb16af788f..5f9b2449552a 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -7,8 +7,8 @@ )] #![warn(clippy::must_use_candidate)] use std::rc::Rc; -use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; pub struct MyAtomic(AtomicBool); pub struct MyPure; diff --git a/src/tools/clippy/tests/ui/mut_key.rs b/src/tools/clippy/tests/ui/mut_key.rs index 81d8732b3b21..43ff705c4775 100644 --- a/src/tools/clippy/tests/ui/mut_key.rs +++ b/src/tools/clippy/tests/ui/mut_key.rs @@ -2,9 +2,9 @@ use std::cell::Cell; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use std::hash::{Hash, Hasher}; use std::rc::Rc; +use std::sync::Arc; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering::Relaxed; -use std::sync::Arc; struct Key(AtomicUsize); diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.rs b/src/tools/clippy/tests/ui/needless_pass_by_value.rs index 14cba5a7eb52..9408b8c948fe 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_value.rs +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.rs @@ -84,7 +84,7 @@ trait Serialize {} impl<'a, T> Serialize for &'a T where T: Serialize {} impl Serialize for i32 {} -fn test_blanket_ref(_foo: T, _serializable: S) {} +fn test_blanket_ref(vals: T, serializable: S) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body fn issue_2114(s: String, t: String, u: Vec, v: Vec) { @@ -116,7 +116,7 @@ impl S { ) { } - fn baz(&self, _u: U, _s: Self) {} + fn baz(&self, uu: U, ss: Self) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body //~| ERROR: this argument is passed by value, but not consumed in the function body } @@ -162,13 +162,13 @@ fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { // The following 3 lines should not cause an ICE. See #2831 trait Bar<'a, A> {} impl<'b, T> Bar<'b, T> for T {} -fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} +fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body // Also this should not cause an ICE. See #2831 trait Club<'a, A> {} impl Club<'static, T> for T {} -fn more_fun(_item: impl Club<'static, i32>) {} +fn more_fun(items: impl Club<'static, i32>) {} //~^ ERROR: this argument is passed by value, but not consumed in the function body fn is_sync(_: T) @@ -177,6 +177,10 @@ where { } +struct Obj(String); + +fn prefix_test(_unused_with_prefix: Obj) {} + fn main() { // This should not cause an ICE either // https://github.com/rust-lang/rust-clippy/issues/3144 diff --git a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr index 827a200ba681..46ef8f3e8da4 100644 --- a/src/tools/clippy/tests/ui/needless_pass_by_value.stderr +++ b/src/tools/clippy/tests/ui/needless_pass_by_value.stderr @@ -46,7 +46,7 @@ LL | fn test_destructure(x: Wrapper, y: Wrapper, z: Wrapper) { error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:87:49 | -LL | fn test_blanket_ref(_foo: T, _serializable: S) {} +LL | fn test_blanket_ref(vals: T, serializable: S) {} | ^ help: consider taking a reference instead: `&T` error: this argument is passed by value, but not consumed in the function body @@ -106,13 +106,13 @@ LL | t: String, error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:119:23 | -LL | fn baz(&self, _u: U, _s: Self) {} +LL | fn baz(&self, uu: U, ss: Self) {} | ^ help: consider taking a reference instead: `&U` error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:119:30 | -LL | fn baz(&self, _u: U, _s: Self) {} +LL | fn baz(&self, uu: U, ss: Self) {} | ^^^^ help: consider taking a reference instead: `&Self` error: this argument is passed by value, but not consumed in the function body @@ -121,7 +121,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn bar_copy(x: u32, y: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -133,7 +133,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -145,7 +145,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -157,7 +157,7 @@ error: this argument is passed by value, but not consumed in the function body LL | fn test_destructure_copy(x: CopyWrapper, y: CopyWrapper, z: CopyWrapper) { | ^^^^^^^^^^^ help: consider taking a reference instead: `&CopyWrapper` | -help: consider marking this type as `Copy` +help: or consider marking this type as `Copy` --> tests/ui/needless_pass_by_value.rs:141:1 | LL | struct CopyWrapper(u32); @@ -166,13 +166,13 @@ LL | struct CopyWrapper(u32); error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:165:40 | -LL | fn some_fun<'b, S: Bar<'b, ()>>(_item: S) {} +LL | fn some_fun<'b, S: Bar<'b, ()>>(items: S) {} | ^ help: consider taking a reference instead: `&S` error: this argument is passed by value, but not consumed in the function body --> tests/ui/needless_pass_by_value.rs:171:20 | -LL | fn more_fun(_item: impl Club<'static, i32>) {} +LL | fn more_fun(items: impl Club<'static, i32>) {} | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider taking a reference instead: `&impl Club<'static, i32>` error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed index fc4129e1db84..c5c570690b45 100644 --- a/src/tools/clippy/tests/ui/needless_return.fixed +++ b/src/tools/clippy/tests/ui/needless_return.fixed @@ -355,4 +355,8 @@ fn conjunctive_blocks() -> String { ({ "a".to_string() } + "b" + { "c" }) } +fn issue12907() -> String { + "".split("").next().unwrap().to_string() +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs index 61c7a02008f0..738611391dfd 100644 --- a/src/tools/clippy/tests/ui/needless_return.rs +++ b/src/tools/clippy/tests/ui/needless_return.rs @@ -365,4 +365,8 @@ fn conjunctive_blocks() -> String { return { "a".to_string() } + "b" + { "c" }; } +fn issue12907() -> String { + return "".split("").next().unwrap().to_string(); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr index ea9c230eafd2..da0fa220d8c4 100644 --- a/src/tools/clippy/tests/ui/needless_return.stderr +++ b/src/tools/clippy/tests/ui/needless_return.stderr @@ -665,5 +665,17 @@ LL - return { "a".to_string() } + "b" + { "c" }; LL + ({ "a".to_string() } + "b" + { "c" }) | -error: aborting due to 53 previous errors +error: unneeded `return` statement + --> tests/ui/needless_return.rs:369:5 + | +LL | return "".split("").next().unwrap().to_string(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: remove `return` + | +LL - return "".split("").next().unwrap().to_string(); +LL + "".split("").next().unwrap().to_string() + | + +error: aborting due to 54 previous errors diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed index 237f5f5b97a4..f68d5e30d27e 100644 --- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.fixed @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs index c8da5dbcec28..647c3769d2c3 100644 --- a/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs +++ b/src/tools/clippy/tests/ui/non_octal_unix_permissions.rs @@ -1,4 +1,4 @@ -//@ignore-target-windows +//@ignore-target: windows #![warn(clippy::non_octal_unix_permissions)] use std::fs::{DirBuilder, File, OpenOptions, Permissions}; diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.fixed b/src/tools/clippy/tests/ui/non_zero_suggestions.fixed new file mode 100644 index 000000000000..e67c992fdde0 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_zero_suggestions.fixed @@ -0,0 +1,65 @@ +#![warn(clippy::non_zero_suggestions)] +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + /// Positive test cases (lint should trigger) + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let r2 = x % NonZeroU64::from(y); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / NonZeroU32::from(b); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + + let x = NonZeroU64::from(NonZeroU32::new(5).unwrap()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; + + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r7 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r8 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r9 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / NonZeroU64::from(y) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / NonZeroU64::from(divisor) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + } +} diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.rs b/src/tools/clippy/tests/ui/non_zero_suggestions.rs new file mode 100644 index 000000000000..de82371a8f29 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_zero_suggestions.rs @@ -0,0 +1,65 @@ +#![warn(clippy::non_zero_suggestions)] +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + /// Positive test cases (lint should trigger) + // U32 -> U64 + let x: u64 = 100; + let y = NonZeroU32::new(10).unwrap(); + let r1 = x / u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let r2 = x % u64::from(y.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + // U16 -> U32 + let a: u32 = 50; + let b = NonZeroU16::new(5).unwrap(); + let r3 = a / u32::from(b.get()); + //~^ ERROR: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + + let x = u64::from(NonZeroU32::new(5).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + /// Negative test cases (lint should not trigger) + // Left hand side expressions should not be triggered + let c: u32 = 50; + let d = NonZeroU16::new(5).unwrap(); + let r4 = u32::from(b.get()) / a; + + // Should not trigger for any other operand other than `/` and `%` + let r5 = a + u32::from(b.get()); + let r6 = a - u32::from(b.get()); + + // Same size types + let e: u32 = 200; + let f = NonZeroU32::new(20).unwrap(); + let r7 = e / f.get(); + + // Smaller to larger, but not NonZero + let g: u64 = 1000; + let h: u32 = 50; + let r8 = g / u64::from(h); + + // Using From correctly + let k: u64 = 300; + let l = NonZeroU32::new(15).unwrap(); + let r9 = k / NonZeroU64::from(l); +} + +// Additional function to test the lint in a different context +fn divide_numbers(x: u64, y: NonZeroU32) -> u64 { + x / u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +struct Calculator { + value: u64, +} + +impl Calculator { + fn divide(&self, divisor: NonZeroU32) -> u64 { + self.value / u64::from(divisor.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + } +} diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions.stderr b/src/tools/clippy/tests/ui/non_zero_suggestions.stderr new file mode 100644 index 000000000000..7a57f7983be7 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_zero_suggestions.stderr @@ -0,0 +1,41 @@ +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:9:18 + | +LL | let r1 = x / u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:12:18 + | +LL | let r2 = x % u64::from(y.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: consider using `NonZeroU32::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:18:18 + | +LL | let r3 = a / u32::from(b.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU32::from(b)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:21:13 + | +LL | let x = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:52:9 + | +LL | x / u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions.rs:62:22 + | +LL | self.value / u64::from(divisor.get()) + | ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(divisor)` + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs new file mode 100644 index 000000000000..193c710e589f --- /dev/null +++ b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.rs @@ -0,0 +1,23 @@ +#![warn(clippy::non_zero_suggestions)] +//@no-rustfix +use std::num::{NonZeroI8, NonZeroI16, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize}; + +fn main() { + let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + + let n = NonZeroU32::new(20).unwrap(); + let y = u64::from(n.get()); + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + some_fn_that_only_takes_u64(y); + + let m = NonZeroU32::try_from(1).unwrap(); + let _z: NonZeroU64 = m.into(); +} + +fn return_non_zero(x: u64, y: NonZeroU32) -> u64 { + u64::from(y.get()) + //~^ ERROR: consider using `NonZeroU64::from()` for more efficient and type-safe conversion +} + +fn some_fn_that_only_takes_u64(_: u64) {} diff --git a/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr new file mode 100644 index 000000000000..787179f2a2d6 --- /dev/null +++ b/src/tools/clippy/tests/ui/non_zero_suggestions_unfixable.stderr @@ -0,0 +1,23 @@ +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:6:18 + | +LL | let x: u64 = u64::from(NonZeroU32::new(5).unwrap().get()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(NonZeroU32::new(5).unwrap())` + | + = note: `-D clippy::non-zero-suggestions` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::non_zero_suggestions)]` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:10:13 + | +LL | let y = u64::from(n.get()); + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(n)` + +error: consider using `NonZeroU64::from()` for more efficient and type-safe conversion + --> tests/ui/non_zero_suggestions_unfixable.rs:19:5 + | +LL | u64::from(y.get()) + | ^^^^^^^^^^^^^^^^^^ help: replace with: `NonZeroU64::from(y)` + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/panic_in_result_fn.rs b/src/tools/clippy/tests/ui/panic_in_result_fn.rs index aaaf9a109acb..e2375aa996f7 100644 --- a/src/tools/clippy/tests/ui/panic_in_result_fn.rs +++ b/src/tools/clippy/tests/ui/panic_in_result_fn.rs @@ -71,6 +71,15 @@ fn function_result_with_custom_todo() -> Result // should not emit Ok(true) } +fn issue_13381() -> Result<(), String> { + const { + if N == 0 { + panic!(); + } + } + Ok(()) +} + fn main() -> Result<(), String> { todo!("finish main method"); Ok(()) diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs new file mode 100644 index 000000000000..b5abcbb3474c --- /dev/null +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.rs @@ -0,0 +1,33 @@ +//@ needs-asm-support +#![warn(clippy::pointers_in_nomem_asm_block)] +#![crate_type = "lib"] +#![no_std] + +use core::arch::asm; + +unsafe fn nomem_bad(p: &i32) { + asm!( + "asdf {p1}, {p2}, {p3}", + p1 = in(reg) p, + //~^ ERROR: passing pointers to nomem asm block + p2 = in(reg) p as *const _ as usize, + p3 = in(reg) p, + options(nomem, nostack, preserves_flags) + ); +} + +unsafe fn nomem_good(p: &i32) { + asm!("asdf {p}", p = in(reg) p, options(readonly, nostack, preserves_flags)); + let p = p as *const i32 as usize; + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); +} + +unsafe fn nomem_bad2(p: &mut i32) { + asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + //~^ ERROR: passing pointers to nomem asm block +} + +unsafe fn nomem_fn(p: extern "C" fn()) { + asm!("call {p}", p = in(reg) p, options(nomem)); + //~^ ERROR: passing pointers to nomem asm block +} diff --git a/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr new file mode 100644 index 000000000000..cabeb37344f2 --- /dev/null +++ b/src/tools/clippy/tests/ui/pointers_in_nomem_asm_block.stderr @@ -0,0 +1,34 @@ +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:11:9 + | +LL | p1 = in(reg) p, + | ^^^^^^^^^^^^^^ +... +LL | p3 = in(reg) p, + | ^^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + = note: `-D clippy::pointers-in-nomem-asm-block` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::pointers_in_nomem_asm_block)]` + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:26:22 + | +LL | asm!("asdf {p}", p = in(reg) p, options(nomem, nostack, preserves_flags)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: passing pointers to nomem asm block + --> tests/ui/pointers_in_nomem_asm_block.rs:31:22 + | +LL | asm!("call {p}", p = in(reg) p, options(nomem)); + | ^^^^^^^^^^^^^ + | + = note: `nomem` means that no memory write or read happens inside the asm! block + = note: if this is intentional and no pointers are read or written to, consider allowing the lint + +error: aborting due to 3 previous errors + diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs index e6ef62681212..e8b42d3b913b 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.rs +++ b/src/tools/clippy/tests/ui/ptr_arg.rs @@ -309,3 +309,25 @@ mod issue_11181 { extern "C" fn allowed(_v: &Vec) {} } } + +mod issue_13308 { + use std::ops::Deref; + + fn repro(source: &str, destination: &mut String) { + source.clone_into(destination); + } + fn repro2(source: &str, destination: &mut String) { + ToOwned::clone_into(source, destination); + } + + fn h1(_: &::Target) {} + fn h2(_: T, _: &T::Target) {} + + // Other cases that are still ok to lint and ideally shouldn't regress + fn good(v1: &String, v2: &String) { + //~^ ERROR: writing `&String` instead of `&str` + //~^^ ERROR: writing `&String` instead of `&str` + h1(v1); + h2(String::new(), v2); + } +} diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr index 4246453e64ca..1a6b3aa1f8d4 100644 --- a/src/tools/clippy/tests/ui/ptr_arg.stderr +++ b/src/tools/clippy/tests/ui/ptr_arg.stderr @@ -221,5 +221,17 @@ error: using a reference to `Cow` is not recommended LL | fn cow_bad_ret_ty_2<'a, 'b>(input: &'a Cow<'a, str>) -> &'b str { | ^^^^^^^^^^^^^^^^ help: change this to: `&str` -error: aborting due to 25 previous errors +error: writing `&String` instead of `&str` involves a new object where a slice will do + --> tests/ui/ptr_arg.rs:327:17 + | +LL | fn good(v1: &String, v2: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: writing `&String` instead of `&str` involves a new object where a slice will do + --> tests/ui/ptr_arg.rs:327:30 + | +LL | fn good(v1: &String, v2: &String) { + | ^^^^^^^ help: change this to: `&str` + +error: aborting due to 27 previous errors diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed index 21ac42196e1b..9a5272c7adc3 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed @@ -68,3 +68,20 @@ fn _msrv_1_65() { let _ = ptr.cast_mut(); let _ = mut_ptr.cast_const(); } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + let _ = std::ptr::null_mut::(); + let _ = std::ptr::null::(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(std::ptr::null_mut::()); + let _ = inline!(std::ptr::null_mut::()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs index 5ce590b2b7e4..43ab5f5ba7cc 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs @@ -68,3 +68,20 @@ fn _msrv_1_65() { let _ = ptr as *mut u32; let _ = mut_ptr as *const u32; } + +#[inline_macros] +fn null_pointers() { + use std::ptr; + let _ = ptr::null::() as *mut String; + let _ = ptr::null_mut::() as *const u32; + let _ = ptr::null::().cast_mut(); + let _ = ptr::null_mut::().cast_const(); + + // Make sure the lint is triggered inside a macro + let _ = inline!(ptr::null::() as *mut u32); + let _ = inline!(ptr::null::().cast_mut()); + + // Do not lint inside macros from external crates + let _ = external!(ptr::null::() as *mut u32); + let _ = external!(ptr::null::().cast_mut()); +} diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr index 2c52ebd3464d..a693793a4ae9 100644 --- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr +++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr @@ -43,5 +43,45 @@ error: `as` casting between raw pointers while changing only its constness LL | let _ = mut_ptr as *const u32; | ^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `mut_ptr.cast_const()` -error: aborting due to 7 previous errors +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:75:13 + | +LL | let _ = ptr::null::() as *mut String; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: `as` casting to make a mutable null pointer into a const null pointer + --> tests/ui/ptr_cast_constness.rs:76:13 + | +LL | let _ = ptr::null_mut::() as *const u32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:77:13 + | +LL | let _ = ptr::null::().cast_mut(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:78:13 + | +LL | let _ = ptr::null_mut::().cast_const(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null()` directly instead: `std::ptr::null::()` + +error: `as` casting to make a const null pointer into a mutable null pointer + --> tests/ui/ptr_cast_constness.rs:81:21 + | +LL | let _ = inline!(ptr::null::() as *mut u32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: changing constness of a null pointer + --> tests/ui/ptr_cast_constness.rs:82:21 + | +LL | let _ = inline!(ptr::null::().cast_mut()); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `null_mut()` directly instead: `std::ptr::null_mut::()` + | + = note: this error originates in the macro `__inline_mac_fn_null_pointers` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 13 previous errors diff --git a/src/tools/clippy/tests/ui/result_large_err.rs b/src/tools/clippy/tests/ui/result_large_err.rs index b25348bf9961..9c39f023da2d 100644 --- a/src/tools/clippy/tests/ui/result_large_err.rs +++ b/src/tools/clippy/tests/ui/result_large_err.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #![warn(clippy::result_large_err)] #![allow(clippy::large_enum_variant)] diff --git a/src/tools/clippy/tests/ui/single_call_fn.rs b/src/tools/clippy/tests/ui/single_call_fn.rs index 55e7508a9573..a0597664da55 100644 --- a/src/tools/clippy/tests/ui/single_call_fn.rs +++ b/src/tools/clippy/tests/ui/single_call_fn.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 //@aux-build:proc_macros.rs #![allow(clippy::redundant_closure_call, unused)] #![warn(clippy::single_call_fn)] diff --git a/src/tools/clippy/tests/ui/single_match.fixed b/src/tools/clippy/tests/ui/single_match.fixed index 5249c4408899..4016b2699d6b 100644 --- a/src/tools/clippy/tests/ui/single_match.fixed +++ b/src/tools/clippy/tests/ui/single_match.fixed @@ -39,8 +39,8 @@ enum Foo { Bar, Baz(u8), } -use std::borrow::Cow; use Foo::*; +use std::borrow::Cow; fn single_match_know_enum() { let x = Some(1u8); @@ -296,3 +296,27 @@ fn issue11365() { if let Some(A | B) = &Some(A) { println!() } } + +#[derive(Eq, PartialEq)] +pub struct Data([u8; 4]); + +const DATA: Data = Data([1, 2, 3, 4]); +const CONST_I32: i32 = 1; + +fn irrefutable_match() { + println!(); + + println!(); + + let i = 0; + { + let a = 1; + let b = 2; + } + + + + + + println!() +} diff --git a/src/tools/clippy/tests/ui/single_match.rs b/src/tools/clippy/tests/ui/single_match.rs index 882098a56e78..75edaa606053 100644 --- a/src/tools/clippy/tests/ui/single_match.rs +++ b/src/tools/clippy/tests/ui/single_match.rs @@ -51,8 +51,8 @@ enum Foo { Bar, Baz(u8), } -use std::borrow::Cow; use Foo::*; +use std::borrow::Cow; fn single_match_know_enum() { let x = Some(1u8); @@ -360,3 +360,45 @@ fn issue11365() { None | Some(_) => {}, } } + +#[derive(Eq, PartialEq)] +pub struct Data([u8; 4]); + +const DATA: Data = Data([1, 2, 3, 4]); +const CONST_I32: i32 = 1; + +fn irrefutable_match() { + match DATA { + DATA => println!(), + _ => {}, + } + + match CONST_I32 { + CONST_I32 => println!(), + _ => {}, + } + + let i = 0; + match i { + i => { + let a = 1; + let b = 2; + }, + _ => {}, + } + + match i { + i => {}, + _ => {}, + } + + match i { + i => (), + _ => (), + } + + match CONST_I32 { + CONST_I32 => println!(), + _ => {}, + } +} diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr index ceb2a193bf7b..9240b09c50a9 100644 --- a/src/tools/clippy/tests/ui/single_match.stderr +++ b/src/tools/clippy/tests/ui/single_match.stderr @@ -216,5 +216,70 @@ LL | | None | Some(_) => {}, LL | | } | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }` -error: aborting due to 20 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:371:5 + | +LL | / match DATA { +LL | | DATA => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!();` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:376:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!();` + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:382:5 + | +LL | / match i { +LL | | i => { +LL | | let a = 1; +LL | | let b = 2; +LL | | }, +LL | | _ => {}, +LL | | } + | |_____^ + | +help: try + | +LL ~ { +LL + let a = 1; +LL + let b = 2; +LL + } + | + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:390:5 + | +LL | / match i { +LL | | i => {}, +LL | | _ => {}, +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:395:5 + | +LL | / match i { +LL | | i => (), +LL | | _ => (), +LL | | } + | |_____^ help: `match` expression can be removed + +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match.rs:400:5 + | +LL | / match CONST_I32 { +LL | | CONST_I32 => println!(), +LL | | _ => {}, +LL | | } + | |_____^ help: try: `println!()` + +error: aborting due to 26 previous errors diff --git a/src/tools/clippy/tests/ui/single_match_else.fixed b/src/tools/clippy/tests/ui/single_match_else.fixed index 163be16ad8be..c2ca746976bd 100644 --- a/src/tools/clippy/tests/ui/single_match_else.fixed +++ b/src/tools/clippy/tests/ui/single_match_else.fixed @@ -171,3 +171,7 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + Some(&NODE) +} diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs index 3f1fd2b31832..2d9e877ee0fe 100644 --- a/src/tools/clippy/tests/ui/single_match_else.rs +++ b/src/tools/clippy/tests/ui/single_match_else.rs @@ -199,3 +199,13 @@ fn issue_10808(bar: Option) { }, } } + +fn irrefutable_match() -> Option<&'static ExprNode> { + match ExprNode::Butterflies { + ExprNode::Butterflies => Some(&NODE), + _ => { + let x = 5; + None + }, + } +} diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr index 61c348260d05..a2801751a430 100644 --- a/src/tools/clippy/tests/ui/single_match_else.stderr +++ b/src/tools/clippy/tests/ui/single_match_else.stderr @@ -197,5 +197,17 @@ LL + println!("None"); LL + } | -error: aborting due to 9 previous errors +error: this pattern is irrefutable, `match` is useless + --> tests/ui/single_match_else.rs:204:5 + | +LL | / match ExprNode::Butterflies { +LL | | ExprNode::Butterflies => Some(&NODE), +LL | | _ => { +LL | | let x = 5; +LL | | None +LL | | }, +LL | | } + | |_____^ help: try: `Some(&NODE)` + +error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/string_slice.rs b/src/tools/clippy/tests/ui/string_slice.rs index 1d1911aaa1d9..dc519493a4dd 100644 --- a/src/tools/clippy/tests/ui/string_slice.rs +++ b/src/tools/clippy/tests/ui/string_slice.rs @@ -1,7 +1,7 @@ -use std::borrow::Cow; +#![warn(clippy::string_slice)] +#![allow(clippy::no_effect)] -#[warn(clippy::string_slice)] -#[allow(clippy::no_effect)] +use std::borrow::Cow; fn main() { &"Ölkanne"[1..]; diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed b/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed index 5d7b1e0c17f2..704d6ea1bb83 100644 --- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed +++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.fixed @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap(); diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs index 8abd9803a0c6..2a2a7557381c 100644 --- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs +++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.rs @@ -1,3 +1,4 @@ +#![allow(clippy::zombie_processes)] fn main() { // Things it should warn about: std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); diff --git a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr index d2517b66b566..6fd07d07d7be 100644 --- a/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr +++ b/src/tools/clippy/tests/ui/suspicious_command_arg_space.stderr @@ -1,5 +1,5 @@ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:3:44 + --> tests/ui/suspicious_command_arg_space.rs:4:44 | LL | std::process::Command::new("echo").arg("-n hello").spawn().unwrap(); | ^^^^^^^^^^ @@ -12,7 +12,7 @@ LL | std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap | ~~~~ ~~~~~~~~~~~~~~~ error: single argument that looks like it should be multiple arguments - --> tests/ui/suspicious_command_arg_space.rs:6:43 + --> tests/ui/suspicious_command_arg_space.rs:7:43 | LL | std::process::Command::new("cat").arg("--number file").spawn().unwrap(); | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/suspicious_to_owned.rs b/src/tools/clippy/tests/ui/suspicious_to_owned.rs index f32b07d45f63..794c2e7174af 100644 --- a/src/tools/clippy/tests/ui/suspicious_to_owned.rs +++ b/src/tools/clippy/tests/ui/suspicious_to_owned.rs @@ -3,7 +3,7 @@ #![warn(clippy::implicit_clone)] #![allow(clippy::redundant_clone)] use std::borrow::Cow; -use std::ffi::{c_char, CStr}; +use std::ffi::{CStr, c_char}; fn main() { let moo = "Moooo"; diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed index 26cc5c27e88c..3536c1746df3 100644 --- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.fixed @@ -1,5 +1,4 @@ #![warn(clippy::tabs_in_doc_comments)] -#[allow(dead_code)] /// /// Struct to hold two strings: diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs index 14b06966ecc1..033a685066e1 100644 --- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.rs @@ -1,5 +1,4 @@ #![warn(clippy::tabs_in_doc_comments)] -#[allow(dead_code)] /// /// Struct to hold two strings: diff --git a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr index aef6c3914526..f8d30b728e58 100644 --- a/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr +++ b/src/tools/clippy/tests/ui/tabs_in_doc_comments.stderr @@ -1,5 +1,5 @@ error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:5 + --> tests/ui/tabs_in_doc_comments.rs:5:5 | LL | /// - first one | ^^^^ help: consider using four spaces per tab @@ -8,43 +8,43 @@ LL | /// - first one = help: to override `-D warnings` add `#[allow(clippy::tabs_in_doc_comments)]` error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:6:13 + --> tests/ui/tabs_in_doc_comments.rs:5:13 | LL | /// - first one | ^^^^^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:5 + --> tests/ui/tabs_in_doc_comments.rs:6:5 | LL | /// - second one | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:7:14 + --> tests/ui/tabs_in_doc_comments.rs:6:14 | LL | /// - second one | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:10:9 + --> tests/ui/tabs_in_doc_comments.rs:9:9 | LL | /// - First String: | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:11:9 + --> tests/ui/tabs_in_doc_comments.rs:10:9 | LL | /// - needs to be inside here | ^^^^^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:14:9 + --> tests/ui/tabs_in_doc_comments.rs:13:9 | LL | /// - Second String: | ^^^^ help: consider using four spaces per tab error: using tabs in doc comments is not recommended - --> tests/ui/tabs_in_doc_comments.rs:15:9 + --> tests/ui/tabs_in_doc_comments.rs:14:9 | LL | /// - needs to be inside here | ^^^^^^^^ help: consider using four spaces per tab diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs index 1042249c5b7b..7d0a37cde46d 100644 --- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs +++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.rs @@ -2,6 +2,16 @@ #![warn(clippy::too_long_first_doc_paragraph)] +pub mod foo { + + // in foo.rs + //! A very short summary. + //! A much longer explanation that goes into a lot more detail about + //! how the thing works, possibly with doclinks and so one, + //! and probably spanning a many rows. Blablabla, it needs to be over + //! 200 characters so I needed to write something longeeeeeeer. +} + /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, /// gravida non lacinia at, rhoncus eu lacus. diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr index 7f48e5cf884e..39926647f543 100644 --- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr +++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr @@ -1,16 +1,32 @@ error: first doc comment paragraph is too long - --> tests/ui/too_long_first_doc_paragraph.rs:5:1 + --> tests/ui/too_long_first_doc_paragraph.rs:8:5 + | +LL | / //! A very short summary. +LL | | //! A much longer explanation that goes into a lot more detail about +LL | | //! how the thing works, possibly with doclinks and so one, +LL | | //! and probably spanning a many rows. Blablabla, it needs to be over +LL | | //! 200 characters so I needed to write something longeeeeeeer. + | |____^ + | + = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` +help: add an empty line + | +LL ~ //! A very short summary. +LL + //! +LL ~ //! A much longer explanation that goes into a lot more detail about + | + +error: first doc comment paragraph is too long + --> tests/ui/too_long_first_doc_paragraph.rs:15:1 | LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero, LL | | /// gravida non lacinia at, rhoncus eu lacus. | |_ - | - = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]` error: first doc comment paragraph is too long - --> tests/ui/too_long_first_doc_paragraph.rs:26:1 + --> tests/ui/too_long_first_doc_paragraph.rs:36:1 | LL | / /// Lorem LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia @@ -18,5 +34,5 @@ LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris a LL | | /// gravida non lacinia at, rhoncus eu lacus. | |_ -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/src/tools/clippy/tests/ui/transmute_32bit.rs b/src/tools/clippy/tests/ui/transmute_32bit.rs index 8e1316ca39d4..e162bbb2d929 100644 --- a/src/tools/clippy/tests/ui/transmute_32bit.rs +++ b/src/tools/clippy/tests/ui/transmute_32bit.rs @@ -1,4 +1,4 @@ -//@ignore-64bit +//@ignore-bitwidth: 64 #[warn(clippy::wrong_transmute)] fn main() { diff --git a/src/tools/clippy/tests/ui/transmute_64bit.rs b/src/tools/clippy/tests/ui/transmute_64bit.rs index 767cc503a39d..fd0ad74bcfa0 100644 --- a/src/tools/clippy/tests/ui/transmute_64bit.rs +++ b/src/tools/clippy/tests/ui/transmute_64bit.rs @@ -1,4 +1,4 @@ -//@ignore-32bit +//@ignore-bitwidth: 32 #[warn(clippy::wrong_transmute)] fn main() { diff --git a/src/tools/clippy/tests/ui/transmute_collection.rs b/src/tools/clippy/tests/ui/transmute_collection.rs index e30b34a5d7d6..6748b66e0eb6 100644 --- a/src/tools/clippy/tests/ui/transmute_collection.rs +++ b/src/tools/clippy/tests/ui/transmute_collection.rs @@ -2,7 +2,7 @@ #![allow(clippy::missing_transmute_annotations)] use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, VecDeque}; -use std::mem::{transmute, MaybeUninit}; +use std::mem::{MaybeUninit, transmute}; fn main() { unsafe { diff --git a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs index dd4bac7f1ed7..5b16d71f1142 100644 --- a/src/tools/clippy/tests/ui/transmute_undefined_repr.rs +++ b/src/tools/clippy/tests/ui/transmute_undefined_repr.rs @@ -8,7 +8,7 @@ use core::any::TypeId; use core::ffi::c_void; -use core::mem::{size_of, transmute, MaybeUninit}; +use core::mem::{MaybeUninit, size_of, transmute}; use core::ptr::NonNull; fn value() -> T { diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index 0cc77a8775d5..464f88140bdb 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -150,4 +150,36 @@ fn main() { vec.set_len(10); } } + + fn nested_union() { + let mut vec: Vec>> = Vec::with_capacity(1); + unsafe { + vec.set_len(1); + } + } + + struct Recursive(*const Recursive, MaybeUninit); + fn recursive_union() { + // Make sure we don't stack overflow on recursive types. + // The pointer acts as the base case because it can't be uninit regardless of its pointee. + + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } + + #[repr(u8)] + enum Enum { + Variant(T), + } + fn union_in_enum() { + // Enums can have a discriminant that can't be uninit, so this should still warn + let mut vec: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { + vec.set_len(1); + } + } } diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index e8b77d653f08..e7c81cf792f2 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -125,5 +125,27 @@ LL | vec.set_len(10); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 12 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:166:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:179:9 + | +LL | let mut vec: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | vec.set_len(1); + | ^^^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 14 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed b/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed new file mode 100644 index 000000000000..7202e1bbd179 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.fixed @@ -0,0 +1,22 @@ +#![warn(clippy::unnecessary_first_then_check)] +#![allow(clippy::useless_vec, clippy::const_is_empty)] + +fn main() { + let s = [1, 2, 3]; + let _: bool = !s.is_empty(); + let _: bool = s.is_empty(); + + let v = vec![1, 2, 3]; + let _: bool = !v.is_empty(); + + let n = [[1, 2, 3], [4, 5, 6]]; + let _: bool = !n[0].is_empty(); + let _: bool = n[0].is_empty(); + + struct Foo { + bar: &'static [i32], + } + let f = [Foo { bar: &[] }]; + let _: bool = !f[0].bar.is_empty(); + let _: bool = f[0].bar.is_empty(); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs b/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs new file mode 100644 index 000000000000..762b95999288 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.rs @@ -0,0 +1,22 @@ +#![warn(clippy::unnecessary_first_then_check)] +#![allow(clippy::useless_vec, clippy::const_is_empty)] + +fn main() { + let s = [1, 2, 3]; + let _: bool = s.first().is_some(); + let _: bool = s.first().is_none(); + + let v = vec![1, 2, 3]; + let _: bool = v.first().is_some(); + + let n = [[1, 2, 3], [4, 5, 6]]; + let _: bool = n[0].first().is_some(); + let _: bool = n[0].first().is_none(); + + struct Foo { + bar: &'static [i32], + } + let f = [Foo { bar: &[] }]; + let _: bool = f[0].bar.first().is_some(); + let _: bool = f[0].bar.first().is_none(); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr b/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr new file mode 100644 index 000000000000..bbaf7e68edab --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_first_then_check.stderr @@ -0,0 +1,47 @@ +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:6:19 + | +LL | let _: bool = s.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!s.is_empty()` + | + = note: `-D clippy::unnecessary-first-then-check` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_first_then_check)]` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:7:21 + | +LL | let _: bool = s.first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:10:19 + | +LL | let _: bool = v.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^ help: replace this with: `!v.is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:13:19 + | +LL | let _: bool = n[0].first().is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!n[0].is_empty()` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:14:24 + | +LL | let _: bool = n[0].first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: unnecessary use of `first().is_some()` to check if slice is not empty + --> tests/ui/unnecessary_first_then_check.rs:20:19 + | +LL | let _: bool = f[0].bar.first().is_some(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `!f[0].bar.is_empty()` + +error: unnecessary use of `first().is_none()` to check if slice is empty + --> tests/ui/unnecessary_first_then_check.rs:21:28 + | +LL | let _: bool = f[0].bar.first().is_none(); + | ^^^^^^^^^^^^^^^^^ help: replace this with: `is_empty()` + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed b/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed index 392f6dd1fee4..1f3e131516ce 100644 --- a/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_min_or_max.fixed @@ -65,3 +65,32 @@ fn random_u32() -> u32 { // random number generator 0 } + +struct Issue13191 { + min: u16, + max: u16, +} + +impl Issue13191 { + fn new() -> Self { + Self { min: 0, max: 0 } + } + + fn min(mut self, value: u16) -> Self { + self.min = value; + self + } + + fn max(mut self, value: u16) -> Self { + self.max = value; + self + } +} + +fn issue_13191() { + // should not fixed + Issue13191::new().min(0); + + // should not fixed + Issue13191::new().max(0); +} diff --git a/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs b/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs index b03755e6d23d..58356b9d49e7 100644 --- a/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs +++ b/src/tools/clippy/tests/ui/unnecessary_min_or_max.rs @@ -65,3 +65,32 @@ fn random_u32() -> u32 { // random number generator 0 } + +struct Issue13191 { + min: u16, + max: u16, +} + +impl Issue13191 { + fn new() -> Self { + Self { min: 0, max: 0 } + } + + fn min(mut self, value: u16) -> Self { + self.min = value; + self + } + + fn max(mut self, value: u16) -> Self { + self.max = value; + self + } +} + +fn issue_13191() { + // should not fixed + Issue13191::new().min(0); + + // should not fixed + Issue13191::new().max(0); +} diff --git a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs index e0e0ded140fc..e9e6c8312f5e 100644 --- a/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs +++ b/src/tools/clippy/tests/ui/unsafe_removed_from_name.rs @@ -7,7 +7,7 @@ use std::cell::UnsafeCell as TotallySafeCell; //~| NOTE: `-D clippy::unsafe-removed-from-name` implied by `-D warnings` use std::cell::UnsafeCell as TotallySafeCellAgain; -//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain +//~^ ERROR: removed `unsafe` from the name of `UnsafeCell` in use as `TotallySafeCellAgain` // Shouldn't error use std::cell::RefCell as ProbablyNotUnsafe; diff --git a/src/tools/clippy/tests/ui/unused_trait_names.fixed b/src/tools/clippy/tests/ui/unused_trait_names.fixed new file mode 100644 index 000000000000..7dfd0db65aa1 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_trait_names.fixed @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as _, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any as _; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any as _; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait as _}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any as _; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/src/tools/clippy/tests/ui/unused_trait_names.rs b/src/tools/clippy/tests/ui/unused_trait_names.rs new file mode 100644 index 000000000000..ce44eedbc79c --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_trait_names.rs @@ -0,0 +1,286 @@ +//@aux-build:proc_macros.rs + +#![allow(unused)] +#![warn(clippy::unused_trait_names)] +#![feature(decl_macro)] + +extern crate proc_macros; + +fn main() {} + +fn bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); +} + +fn good() { + use std::any::Any as _; + + println!("{:?}", "foo".type_id()); +} + +fn used_good() { + use std::any::Any; + + println!("{:?}", Any::type_id("foo")); + println!("{:?}", "foo".type_id()); +} + +fn multi_bad() { + use std::any::{self, Any, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn multi_good() { + use std::any::{self, Any as _, TypeId}; + + println!("{:?}", "foo".type_id()); +} + +fn renamed_bad() { + use std::any::Any as MyAny; + + println!("{:?}", "foo".type_id()); +} + +fn multi_renamed_bad() { + use std::any::{Any as MyAny, TypeId as MyTypeId}; + + println!("{:?}", "foo".type_id()); +} + +mod pub_good { + pub use std::any::Any; + + fn foo() { + println!("{:?}", "foo".type_id()); + } +} + +mod used_mod_good { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } +} + +mod mod_import_bad { + fn mod_import_bad() { + use std::any::Any; + + println!("{:?}", "foo".type_id()); + } +} + +mod nested_mod_used_good1 { + use std::any::Any; + + mod foo { + fn foo() { + super::Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good2 { + use std::any::Any; + + mod foo { + use super::Any; + + fn foo() { + Any::type_id("foo"); + } + } +} + +mod nested_mod_used_good3 { + use std::any::Any; + + mod foo { + use crate::nested_mod_used_good3::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +mod nested_mod_used_bad { + use std::any::Any; + + fn bar() { + println!("{:?}", "foo".type_id()); + } + + mod foo { + use std::any::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } +} + +// More complex example where `use std::any::Any;` should be anonymised but `use std::any::Any as +// MyAny;` should not as it is used by a sub module. Even though if you removed `use std::any::Any;` +// the code would still compile. +mod nested_mod_used_bad1 { + use std::any::Any; + + use std::any::Any as MyAny; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod foo { + use crate::nested_mod_used_bad1::MyAny; + + fn foo() { + println!("{:?}", MyAny::type_id("foo")); + } + } +} + +// Example of nested import with an unused import to try and trick it +mod nested_mod_used_good5 { + use std::any::Any; + + mod foo { + use std::any::Any; + + fn baz() { + println!("{:?}", "baz".type_id()); + } + + mod bar { + use crate::nested_mod_used_good5::foo::Any; + + fn foo() { + println!("{:?}", Any::type_id("foo")); + } + } + } +} + +mod simple_trait { + pub trait MyTrait { + fn do_things(&self); + } + + pub struct MyStruct; + + impl MyTrait for MyStruct { + fn do_things(&self) {} + } +} + +// Underscore imports were stabilized in 1.33 +#[clippy::msrv = "1.32"] +fn msrv_1_32() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +#[clippy::msrv = "1.33"] +fn msrv_1_33() { + use simple_trait::{MyStruct, MyTrait}; + MyStruct.do_things(); +} + +mod lint_inside_macro_expansion_bad { + macro_rules! foo { + () => { + use std::any::Any; + fn bar() { + "bar".type_id(); + } + }; + } + + foo!(); +} + +mod macro_and_trait_same_name { + pub macro Foo() {} + pub trait Foo { + fn bar(&self); + } + impl Foo for () { + fn bar(&self) {} + } +} + +fn call_macro_and_trait_good() { + // importing trait and macro but only using macro by path won't allow us to change this to + // `use macro_and_trait_same_name::Foo as _;` + use macro_and_trait_same_name::Foo; + Foo!(); + ().bar(); +} + +proc_macros::external!( + fn ignore_inside_external_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +proc_macros::with_span!( + span + + fn ignore_inside_with_span_proc_macro() { + use std::any::Any; + "foo".type_id(); + } +); + +// This should warn the import is unused but should not trigger unused_trait_names +#[warn(unused)] +mod unused_import { + use std::any::Any; +} + +#[allow(clippy::unused_trait_names)] +fn allow_lint_fn() { + use std::any::Any; + + "foo".type_id(); +} + +#[allow(clippy::unused_trait_names)] +mod allow_lint_mod { + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +mod allow_lint_import { + #[allow(clippy::unused_trait_names)] + use std::any::Any; + + fn foo() { + "foo".type_id(); + } +} + +// Limitation: Suggests `use std::any::Any as _::{self};` which looks weird +// fn use_trait_self_good() { +// use std::any::Any::{self}; +// "foo".type_id(); +// } + +// Limitation: Suggests `use std::any::{Any as _, Any as _};` +// mod repeated_renamed { +// use std::any::{Any, Any as MyAny}; + +// fn foo() { +// "foo".type_id(); +// } +// } diff --git a/src/tools/clippy/tests/ui/unused_trait_names.stderr b/src/tools/clippy/tests/ui/unused_trait_names.stderr new file mode 100644 index 000000000000..f59d8f58a170 --- /dev/null +++ b/src/tools/clippy/tests/ui/unused_trait_names.stderr @@ -0,0 +1,73 @@ +error: unused import: `std::any::Any` + --> tests/ui/unused_trait_names.rs:245:9 + | +LL | use std::any::Any; + | ^^^^^^^^^^^^^ + | + = note: `-D unused-imports` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unused_imports)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:12:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + | + = note: `-D clippy::unused-trait-names` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unused_trait_names)]` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:31:26 + | +LL | use std::any::{self, Any, TypeId}; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:43:19 + | +LL | use std::any::Any as MyAny; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:49:20 + | +LL | use std::any::{Any as MyAny, TypeId as MyTypeId}; + | ^^^^^^^^^^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:72:23 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:113:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:132:19 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:191:34 + | +LL | use simple_trait::{MyStruct, MyTrait}; + | ^^^^^^^ help: use: `MyTrait as _` + +error: importing trait that is only used anonymously + --> tests/ui/unused_trait_names.rs:198:27 + | +LL | use std::any::Any; + | ^^^ help: use: `Any as _` +... +LL | foo!(); + | ------ in this macro invocation + | + = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 10 previous errors + diff --git a/src/tools/clippy/tests/ui/used_underscore_binding.stderr b/src/tools/clippy/tests/ui/used_underscore_binding.stderr index 556e1792b3e6..f9e8013d3ad5 100644 --- a/src/tools/clippy/tests/ui/used_underscore_binding.stderr +++ b/src/tools/clippy/tests/ui/used_underscore_binding.stderr @@ -1,10 +1,10 @@ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:23:5 | LL | _foo + 1 | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:22:22 | LL | fn prefix_underscore(_foo: u32) -> u32 { @@ -12,61 +12,61 @@ LL | fn prefix_underscore(_foo: u32) -> u32 { = note: `-D clippy::used-underscore-binding` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::used_underscore_binding)]` -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:28:20 | LL | println!("{}", _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:29:16 | LL | assert_eq!(_foo, _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_foo` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:29:22 | LL | assert_eq!(_foo, _foo); | ^^^^ | -note: `_foo` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:27:24 | LL | fn in_macro_or_desugar(_foo: u32) { | ^^^^ -error: used binding `_underscore_field` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:42:5 | LL | s._underscore_field += 1; | ^^^^^^^^^^^^^^^^^^^ | -note: `_underscore_field` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:36:5 | LL | _underscore_field: u32, | ^^^^^^^^^^^^^^^^^^^^^^ -error: used binding `_i` which is prefixed with an underscore. A leading underscore signals that a binding will not be used +error: used underscore-prefixed binding --> tests/ui/used_underscore_binding.rs:103:16 | LL | uses_i(_i); | ^^ | -note: `_i` is defined here +note: binding is defined here --> tests/ui/used_underscore_binding.rs:102:13 | LL | let _i = 5; diff --git a/src/tools/clippy/tests/ui/used_underscore_items.rs b/src/tools/clippy/tests/ui/used_underscore_items.rs new file mode 100644 index 000000000000..223016a5c966 --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_items.rs @@ -0,0 +1,63 @@ +//@aux-build:external_item.rs +#![allow(unused)] +#![warn(clippy::used_underscore_items)] + +extern crate external_item; + +// should not lint macro +macro_rules! macro_wrap_func { + () => { + fn _marco_foo() {} + }; +} + +macro_wrap_func!(); + +struct _FooStruct {} + +impl _FooStruct { + fn _method_call(self) {} +} + +fn _foo1() {} + +fn _foo2() -> i32 { + 0 +} + +mod a { + pub mod b { + pub mod c { + pub fn _foo3() {} + + pub struct _FooStruct2 {} + + impl _FooStruct2 { + pub fn _method_call(self) {} + } + } + } +} + +fn main() { + _foo1(); + let _ = _foo2(); + a::b::c::_foo3(); + let _ = &_FooStruct {}; + let _ = _FooStruct {}; + + let foo_struct = _FooStruct {}; + foo_struct._method_call(); + + let foo_struct2 = a::b::c::_FooStruct2 {}; + foo_struct2._method_call(); +} + +// should not lint exteranl crate. +// user cannot control how others name their items +fn external_item_call() { + let foo_struct3 = external_item::_ExternalStruct {}; + foo_struct3._foo(); + + external_item::_exernal_foo(); +} diff --git a/src/tools/clippy/tests/ui/used_underscore_items.stderr b/src/tools/clippy/tests/ui/used_underscore_items.stderr new file mode 100644 index 000000000000..93ac3a6fec6b --- /dev/null +++ b/src/tools/clippy/tests/ui/used_underscore_items.stderr @@ -0,0 +1,112 @@ +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:43:5 + | +LL | _foo1(); + | ^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:22:1 + | +LL | fn _foo1() {} + | ^^^^^^^^^^ + = note: `-D clippy::used-underscore-items` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::used_underscore_items)]` + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:44:13 + | +LL | let _ = _foo2(); + | ^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:24:1 + | +LL | fn _foo2() -> i32 { + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:45:5 + | +LL | a::b::c::_foo3(); + | ^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:31:13 + | +LL | pub fn _foo3() {} + | ^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:46:14 + | +LL | let _ = &_FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:47:13 + | +LL | let _ = _FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:49:22 + | +LL | let foo_struct = _FooStruct {}; + | ^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:16:1 + | +LL | struct _FooStruct {} + | ^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:50:5 + | +LL | foo_struct._method_call(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:19:5 + | +LL | fn _method_call(self) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:52:23 + | +LL | let foo_struct2 = a::b::c::_FooStruct2 {}; + | ^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:33:13 + | +LL | pub struct _FooStruct2 {} + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: used underscore-prefixed item + --> tests/ui/used_underscore_items.rs:53:5 + | +LL | foo_struct2._method_call(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: item is defined here + --> tests/ui/used_underscore_items.rs:36:17 + | +LL | pub fn _method_call(self) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 9 previous errors + diff --git a/src/tools/clippy/tests/ui/zombie_processes.rs b/src/tools/clippy/tests/ui/zombie_processes.rs new file mode 100644 index 000000000000..a2abc7fc3a17 --- /dev/null +++ b/src/tools/clippy/tests/ui/zombie_processes.rs @@ -0,0 +1,138 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::if_same_then_else, clippy::ifs_same_cond)] + +use std::process::{Child, Command}; + +fn main() { + { + // Check that #[expect] works + #[expect(clippy::zombie_processes)] + let mut x = Command::new("").spawn().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + x.kill(); + x.id(); + } + { + let mut x = Command::new("").spawn().unwrap(); + x.wait().unwrap(); // OK + } + { + let x = Command::new("").spawn().unwrap(); + x.wait_with_output().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + x.try_wait().unwrap(); // OK + } + { + let mut x = Command::new("").spawn().unwrap(); + let mut r = &mut x; + r.wait().unwrap(); // OK, not calling `.wait()` directly on `x` but through `r` -> `x` + } + { + let mut x = Command::new("").spawn().unwrap(); + process_child(x); // OK, other function might call `.wait()` so assume it does + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + let v = &x; + // (allow shared refs is fine because one cannot call `.wait()` through that) + } + + // https://github.com/rust-lang/rust-clippy/pull/11476#issuecomment-1718456033 + // Unconditionally exiting the process in various ways (should not lint) + { + let mut x = Command::new("").spawn().unwrap(); + std::process::exit(0); + } + { + let mut x = Command::new("").spawn().unwrap(); + std::process::abort(); // same as above, but abort instead of exit + } + { + let mut x = Command::new("").spawn().unwrap(); + if true { /* nothing */ } + std::process::abort(); // still unconditionally exits + } + + // Conditionally exiting + // It should assume that it might not exit and still lint + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + std::process::exit(0); + } + } + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + while false {} + // Calling `exit()` after leaving a while loop should still be linted. + std::process::exit(0); + } + } + + { + let mut x = { Command::new("").spawn().unwrap() }; + x.wait().unwrap(); + } + + { + struct S { + c: Child, + } + + let mut s = S { + c: Command::new("").spawn().unwrap(), + }; + s.c.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + return; + } + x.wait().unwrap(); + } + + { + let mut x = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + if true { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + } else if true { + x.wait().unwrap(); + } else { + x.wait().unwrap(); + } + } + + { + let mut x = Command::new("").spawn().unwrap(); + if true { + x.wait().unwrap(); + return; + } + x.wait().unwrap(); + } +} + +fn process_child(c: Child) { + todo!() +} diff --git a/src/tools/clippy/tests/ui/zombie_processes.stderr b/src/tools/clippy/tests/ui/zombie_processes.stderr new file mode 100644 index 000000000000..eec821a4c8f1 --- /dev/null +++ b/src/tools/clippy/tests/ui/zombie_processes.stderr @@ -0,0 +1,64 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:14:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:41:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:66:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:73:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:99:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes.rs:108:21 + | +LL | let mut x = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: consider calling `.wait()` + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 6 previous errors + diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed b/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed new file mode 100644 index 000000000000..6045262f519a --- /dev/null +++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.fixed @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap().wait(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.rs b/src/tools/clippy/tests/ui/zombie_processes_fixable.rs new file mode 100644 index 000000000000..e1ecb771641e --- /dev/null +++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.rs @@ -0,0 +1,26 @@ +#![warn(clippy::zombie_processes)] +#![allow(clippy::needless_return)] + +use std::process::{Child, Command}; + +fn main() { + let _ = Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + Command::new("").spawn().unwrap(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc(); + //~^ ERROR: spawned process is never `wait()`ed on + spawn_proc().wait().unwrap(); // OK +} + +fn not_main() { + Command::new("").spawn().unwrap(); +} + +fn spawn_proc() -> Child { + Command::new("").spawn().unwrap() +} + +fn spawn_proc_2() -> Child { + return Command::new("").spawn().unwrap(); +} diff --git a/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr b/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr new file mode 100644 index 000000000000..e1c40472c325 --- /dev/null +++ b/src/tools/clippy/tests/ui/zombie_processes_fixable.stderr @@ -0,0 +1,40 @@ +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:7:13 + | +LL | let _ = Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + = note: `-D clippy::zombie-processes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::zombie_processes)]` + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:9:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:11:5 + | +LL | spawn_proc(); + | ^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: spawned process is never `wait()`ed on + --> tests/ui/zombie_processes_fixable.rs:17:5 + | +LL | Command::new("").spawn().unwrap(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^- help: try: `.wait()` + | + = note: not doing so might leave behind zombie processes + = note: see https://doc.rust-lang.org/stable/std/process/struct.Child.html#warning + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 99b3560a0642..dcf00e4e384b 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -20,7 +20,6 @@ new_pr = true [assign] contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md" users_on_vacation = [ - "flip1995", "matthiaskrgr", "giraffate", ] diff --git a/src/tools/clippy/util/gh-pages/versions.html b/src/tools/clippy/util/gh-pages/versions.html index 31ce88193295..d38fe39a4640 100644 --- a/src/tools/clippy/util/gh-pages/versions.html +++ b/src/tools/clippy/util/gh-pages/versions.html @@ -1,4 +1,5 @@ + @@ -7,26 +8,15 @@ Clippy lints documentation - -
+
-
- - - -
- - - - - - - + .github-corner:hover .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + @keyframes octocat-wave { + 0%, + 100% { + transform: rotate(0); + } + 20%, + 60% { + transform: rotate(-25deg); + } + 40%, + 80% { + transform: rotate(10deg); + } + } + @media (max-width: 500px) { + .github-corner:hover .octo-arm { + animation: none; + } + .github-corner .octo-arm { + animation: octocat-wave 560ms ease-in-out; + } + } + + diff --git a/src/tools/clippy/util/versions.py b/src/tools/clippy/util/versions.py index c041fc606a8f..fee0d292df16 100755 --- a/src/tools/clippy/util/versions.py +++ b/src/tools/clippy/util/versions.py @@ -1,22 +1,21 @@ #!/usr/bin/env python -import json -import logging as log +from string import Template +import argparse import os import sys -log.basicConfig(level=log.INFO, format="%(levelname)s: %(message)s") - - def key(v): if v == "master": - return float("inf") - if v == "stable": return sys.maxsize - if v == "beta": + if v == "stable": return sys.maxsize - 1 + if v == "beta": + return sys.maxsize - 2 if v == "pre-1.29.0": return -1 + if not v.startswith("rust-"): + return None v = v.replace("rust-", "") @@ -26,26 +25,27 @@ def key(v): return s - def main(): - if len(sys.argv) < 2: - log.error("specify output directory") - return + parser = argparse.ArgumentParser() + parser.add_argument("input", help="path to the versions.html template", type=argparse.FileType("r")) + parser.add_argument("outdir", help="path to write the output HTML") + args = parser.parse_args() - outdir = sys.argv[1] versions = [ dir - for dir in os.listdir(outdir) - if not dir.startswith(".") - and not dir.startswith("v") - and os.path.isdir(os.path.join(outdir, dir)) + for dir in os.listdir(args.outdir) + if key(dir) is not None ] - versions.sort(key=key) + versions.sort(key=key, reverse=True) + links = [f'{version}' for version in versions] - with open(os.path.join(outdir, "versions.json"), "w") as fp: - json.dump(versions, fp, indent=2) - log.info("wrote JSON for great justice") + template = Template(args.input.read()) + html = template.substitute(list="\n".join(links)) + path = os.path.join(args.outdir, "index.html") + with open(path, "w") as out: + out.write(html) + print(f"wrote HTML to {path}") if __name__ == "__main__": main() diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs index 3743446e2764..e13ecbbe54ba 100644 --- a/src/tools/miri/cargo-miri/src/phases.rs +++ b/src/tools/miri/cargo-miri/src/phases.rs @@ -666,6 +666,8 @@ pub fn phase_runner(mut binary_args: impl Iterator, phase: Runner match phase { RunnerPhase::Rustdoc => { cmd.stdin(std::process::Stdio::piped()); + // the warning is wrong, we have a `wait` inside the `scope` closure. + #[expect(clippy::zombie_processes)] let mut child = cmd.spawn().expect("failed to spawn process"); let child_stdin = child.stdin.take().unwrap(); // Write stdin in a background thread, as it may block. diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 79ed6cc7d74b..b05c409f8231 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -1b5aa96d6016bafe50e071b45d4d2e3c90fd766f +76ed7a1fa40c3f54d3fd3f834e12bf9c932d0146 diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs index f792e75ad085..7eb677303838 100644 --- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs +++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs @@ -171,7 +171,7 @@ impl NewPermission { /// F2b: No `SharedReadWrite` or `Unique` will ever be added on top of our `SharedReadOnly`. /// F3: If an access happens with an `&` outside `UnsafeCell`, /// it requires the `SharedReadOnly` to still be in the stack. - +/// /// Core relation on `Permission` to define which accesses are allowed impl Permission { /// This defines for a given permission, whether it permits the given kind of access. diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index bc4d80568726..2b34db047f37 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -748,7 +748,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } ), ); - return Ok(()); + Ok(()) } /// Wake up some thread (if there is any) sleeping on the conditional diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 4efe2beb1558..681e9211246d 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -849,7 +849,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // https://github.com/rust-lang/miri/issues/1763). In this case, // just do nothing, which effectively just returns to the // scheduler. - return Ok(()); + Ok(()) } #[inline] diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 6e015813e77a..4d0606113f9e 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -32,10 +32,7 @@ clippy::derived_hash_with_manual_eq, clippy::too_many_arguments, clippy::type_complexity, - clippy::single_element_loop, - clippy::needless_return, clippy::bool_to_int_with_if, - clippy::box_default, clippy::needless_question_mark, clippy::needless_lifetimes, clippy::too_long_first_doc_paragraph, diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index a33657d33a2e..057d7ef5e67b 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -71,7 +71,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // and not execute any Miri shim. Somewhat unintuitively doing so is done // by returning `NotSupported`, which triggers the `lookup_exported_symbol` // fallback case in `emulate_foreign_item`. - return Ok(EmulateItemResult::NotSupported); + Ok(EmulateItemResult::NotSupported) } AllocatorKind::Default => { default(this)?; diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs index d12a0581b413..c465ef3d28bc 100644 --- a/src/tools/miri/src/shims/env.rs +++ b/src/tools/miri/src/shims/env.rs @@ -103,7 +103,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn get_env_var(&mut self, name: &OsStr) -> InterpResult<'tcx, Option> { let this = self.eval_context_ref(); match &this.machine.env_vars { - EnvVars::Uninit => return Ok(None), + EnvVars::Uninit => Ok(None), EnvVars::Unix(vars) => vars.get(this, name), EnvVars::Windows(vars) => vars.get(name), } diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 6b78ce7ad47b..5fae746af2b6 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -676,12 +676,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_bytes_ptr(buf, bytes[..read_bytes].iter().copied())?; // The actual read size is always less than what got originally requested so this cannot fail. this.write_int(u64::try_from(read_bytes).unwrap(), dest)?; - return Ok(()); + Ok(()) } Err(e) => { this.set_last_error_from_io_error(e)?; this.write_int(-1, dest)?; - return Ok(()); + Ok(()) } } } diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux/epoll.rs index 3d4fe770e99e..675a404ae117 100644 --- a/src/tools/miri/src/shims/unix/linux/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux/epoll.rs @@ -401,19 +401,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { /// list about file descriptors in the interest list that have some /// events available. Up to `maxevents` are returned by `epoll_wait()`. /// The `maxevents` argument must be greater than zero. - + /// /// The `timeout` argument specifies the number of milliseconds that /// `epoll_wait()` will block. Time is measured against the /// CLOCK_MONOTONIC clock. If the timeout is zero, the function will not block, /// while if the timeout is -1, the function will block /// until at least one event has been retrieved (or an error /// occurred). - + /// /// A call to `epoll_wait()` will block until either: /// • a file descriptor delivers an event; /// • the call is interrupted by a signal handler; or /// • the timeout expires. - + /// /// Note that the timeout interval will be rounded up to the system /// clock granularity, and kernel scheduling delays mean that the /// blocking interval may overrun by a small amount. Specifying a @@ -596,7 +596,7 @@ fn ready_list_next( return Some(epoll_event_instance); } } - return None; + None } /// This helper function checks whether an epoll notification should be triggered for a specific @@ -623,9 +623,10 @@ fn check_and_update_one_event_interest<'tcx>( let event_instance = EpollEventInstance::new(flags, epoll_event_interest.data); // Triggers the notification by inserting it to the ready list. ready_list.insert(epoll_key, event_instance); - return Ok(true); + Ok(true) + } else { + Ok(false) } - return Ok(false); } /// Callback function after epoll_wait unblocks diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs index d1d461daa993..9fbabbd96418 100644 --- a/src/tools/miri/src/shims/unix/linux/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs @@ -110,7 +110,7 @@ impl FileDescription for Event { /// write either blocks until a read is performed on the /// file descriptor, or fails with the error EAGAIN if the /// file descriptor has been made nonblocking. - + /// /// A write fails with the error EINVAL if the size of the /// supplied buffer is less than 8 bytes, or if an attempt is /// made to write the value 0xffffffffffffffff. diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 998daddf5208..cd8f3e59015a 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -23,8 +23,8 @@ pub fn is_dyn_sym(name: &str) -> bool { #[cfg(windows)] fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result> { - // We are on Windows so we can simply lte the host do this. - return Ok(path::absolute(path)); + // We are on Windows so we can simply let the host do this. + Ok(path::absolute(path)) } #[cfg(unix)] diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 51b0129356ba..5786b17e50c9 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -90,7 +90,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } ), ); - return Ok(()); + Ok(()) } fn InitOnceComplete( diff --git a/src/tools/rust-analyzer/.typos.toml b/src/tools/rust-analyzer/.typos.toml index e7e764ce0351..febfb233bd97 100644 --- a/src/tools/rust-analyzer/.typos.toml +++ b/src/tools/rust-analyzer/.typos.toml @@ -15,6 +15,7 @@ extend-ignore-re = [ '"flate2"', "raison d'être", "inout", + "INOUT", "optin" ] diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 4e9319f13aa8..dc820fcb28d2 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -96,6 +96,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "cfg_aliases 0.2.1", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -167,6 +176,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chalk-derive" version = "0.98.0" @@ -982,7 +997,7 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" dependencies = [ "crossbeam-channel", "ctrlc", @@ -994,9 +1009,9 @@ dependencies = [ [[package]] name = "lsp-server" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248f65b78f6db5d8e1b1604b4098a28b43d21a8eb1deeca22b1c421b276c7095" +checksum = "550446e84739dcaf6d48a4a093973850669e13e8a34d8f8d64851041be267cd9" dependencies = [ "crossbeam-channel", "log", @@ -1029,8 +1044,10 @@ version = "0.0.0" dependencies = [ "arrayvec", "cov-mark", + "expect-test", "intern", "parser", + "ra-ap-rustc_lexer", "rustc-hash", "smallvec", "span", @@ -1113,7 +1130,7 @@ checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ "bitflags 2.6.0", "cfg-if", - "cfg_aliases", + "cfg_aliases 0.1.1", "libc", ] @@ -1468,9 +1485,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_abi" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011c39d409940a890414e3a7b239762ac16d88029ad71b050a8374831b93790" +checksum = "2a8cb51bb4534ac3e9c74f1d9bd90e607e60f94f734b1cf1a66f753ad2af6ed7" dependencies = [ "bitflags 2.6.0", "ra-ap-rustc_index", @@ -1479,9 +1496,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9027acdee649b0b27eb10b7db5be833efee3362d394935c5eed8f0745a9d43ce" +checksum = "8b640fba2b7ef4f875459e2e76daeb846ef341d1d376fa758962ac0eba79bce6" dependencies = [ "arrayvec", "ra-ap-rustc_index_macros", @@ -1490,9 +1507,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_index_macros" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "540b86dc0384141ac8e825fc2874cd44bffd4277d99d8ec63ee416f1a98d5997" +checksum = "faef502419ba5ac9d3079b1a835c6e5b4e605388254bbe55eb5683936f541be9" dependencies = [ "proc-macro2", "quote", @@ -1501,9 +1518,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_lexer" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdf98bb457b47b9ae4aeebf867d0ca440c86925e0b6381658c4a02589748c9d" +checksum = "5da7f9d533b8d5be6704558da741ff20b982ad4647b1e9e08632853e4fecf9d5" dependencies = [ "unicode-properties", "unicode-xid", @@ -1511,9 +1528,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_parse_format" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8fe3556ab6311bb775220563a300e2bf62ec56404521fe0c511a583937683d5" +checksum = "94389cf81c651b1bda9ac45d3de6a2d851bb6fd4cb893875daa44e419c94205f" dependencies = [ "ra-ap-rustc_index", "ra-ap-rustc_lexer", @@ -1521,9 +1538,9 @@ dependencies = [ [[package]] name = "ra-ap-rustc_pattern_analysis" -version = "0.63.0" +version = "0.68.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1709080fdeb5db630e1c2644026c2962aaa32416cd92f0190c04b0c21e114b91" +checksum = "3679d8dd0114ed6000918309f843782738e51c99d8e4baec0d0f706e4d948819" dependencies = [ "ra-ap-rustc_index", "rustc-hash", @@ -1636,7 +1653,7 @@ dependencies = [ "intern", "itertools", "load-cargo", - "lsp-server 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lsp-server 0.7.7 (registry+https://github.com/rust-lang/crates.io-index)", "lsp-types", "memchr", "mimalloc", @@ -1843,10 +1860,11 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "smol_str" -version = "0.2.2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead" +checksum = "66eaf762c5af19db3108300515c8aa7a50efc90ff745f4c62288052ebf9fdd25" dependencies = [ + "borsh", "serde", ] @@ -2607,6 +2625,7 @@ version = "0.1.0" dependencies = [ "anyhow", "directories", + "either", "flate2", "itertools", "proc-macro2", diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml index aa7bd2dc5fe8..b4587a379618 100644 --- a/src/tools/rust-analyzer/Cargo.toml +++ b/src/tools/rust-analyzer/Cargo.toml @@ -85,11 +85,11 @@ tt = { path = "./crates/tt", version = "0.0.0" } vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" } vfs = { path = "./crates/vfs", version = "0.0.0" } -ra-ap-rustc_lexer = { version = "0.63.0", default-features = false } -ra-ap-rustc_parse_format = { version = "0.63.0", default-features = false } -ra-ap-rustc_index = { version = "0.63.0", default-features = false } -ra-ap-rustc_abi = { version = "0.63.0", default-features = false } -ra-ap-rustc_pattern_analysis = { version = "0.63.0", default-features = false } +ra-ap-rustc_lexer = { version = "0.68.0", default-features = false } +ra-ap-rustc_parse_format = { version = "0.68.0", default-features = false } +ra-ap-rustc_index = { version = "0.68.0", default-features = false } +ra-ap-rustc_abi = { version = "0.68.0", default-features = false } +ra-ap-rustc_pattern_analysis = { version = "0.68.0", default-features = false } # local crates that aren't published to crates.io. These should not have versions. test-fixture = { path = "./crates/test-fixture" } @@ -145,7 +145,7 @@ smallvec = { version = "1.10.0", features = [ "union", "const_generics", ] } -smol_str = "0.2.1" +smol_str = "0.3.1" snap = "1.1.0" text-size = "1.1.1" tracing = "0.1.40" @@ -185,6 +185,7 @@ style = { level = "warn", priority = -1 } suspicious = { level = "warn", priority = -1 } ## allow following lints +too_long_first_doc_paragraph = "allow" # subjective single_match = "allow" # () makes a fine error in most cases diff --git a/src/tools/rust-analyzer/crates/base-db/src/change.rs b/src/tools/rust-analyzer/crates/base-db/src/change.rs index a9d91d64ceb6..4fb6654b612f 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/change.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/change.rs @@ -3,11 +3,15 @@ use std::fmt; +use rustc_hash::FxHashMap; use salsa::Durability; use triomphe::Arc; use vfs::FileId; -use crate::{CrateGraph, SourceDatabaseFileInputExt, SourceRoot, SourceRootDatabase, SourceRootId}; +use crate::{ + CrateGraph, CrateId, CrateWorkspaceData, SourceDatabaseFileInputExt, SourceRoot, + SourceRootDatabase, SourceRootId, +}; /// Encapsulate a bunch of raw `.set` calls on the database. #[derive(Default)] @@ -15,6 +19,7 @@ pub struct FileChange { pub roots: Option>, pub files_changed: Vec<(FileId, Option)>, pub crate_graph: Option, + pub ws_data: Option>>, } impl fmt::Debug for FileChange { @@ -50,6 +55,10 @@ impl FileChange { self.crate_graph = Some(graph); } + pub fn set_ws_data(&mut self, data: FxHashMap>) { + self.ws_data = Some(data); + } + pub fn apply(self, db: &mut dyn SourceRootDatabase) { let _p = tracing::info_span!("FileChange::apply").entered(); if let Some(roots) = self.roots { @@ -74,6 +83,9 @@ impl FileChange { if let Some(crate_graph) = self.crate_graph { db.set_crate_graph_with_durability(Arc::new(crate_graph), Durability::HIGH); } + if let Some(data) = self.ws_data { + db.set_crate_workspace_data_with_durability(Arc::new(data), Durability::HIGH); + } } } diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs index 3616fa9fd868..f5109339ad10 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/input.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs @@ -374,37 +374,6 @@ impl CrateGraph { self.arena.alloc(data) } - /// Remove the crate from crate graph. If any crates depend on this crate, the dependency would be replaced - /// with the second input. - pub fn remove_and_replace( - &mut self, - id: CrateId, - replace_with: CrateId, - ) -> Result<(), CyclicDependenciesError> { - for (x, data) in self.arena.iter() { - if x == id { - continue; - } - for edge in &data.dependencies { - if edge.crate_id == id { - self.check_cycle_after_dependency(edge.crate_id, replace_with)?; - } - } - } - // if everything was ok, start to replace - for (x, data) in self.arena.iter_mut() { - if x == id { - continue; - } - for edge in &mut data.dependencies { - if edge.crate_id == id { - edge.crate_id = replace_with; - } - } - } - Ok(()) - } - pub fn add_dep( &mut self, from: CrateId, @@ -412,26 +381,17 @@ impl CrateGraph { ) -> Result<(), CyclicDependenciesError> { let _p = tracing::info_span!("add_dep").entered(); - self.check_cycle_after_dependency(from, dep.crate_id)?; - - self.arena[from].add_dep(dep); - Ok(()) - } - - /// Check if adding a dep from `from` to `to` creates a cycle. To figure - /// that out, look for a path in the *opposite* direction, from `to` to - /// `from`. - fn check_cycle_after_dependency( - &self, - from: CrateId, - to: CrateId, - ) -> Result<(), CyclicDependenciesError> { - if let Some(path) = self.find_path(&mut FxHashSet::default(), to, from) { + // Check if adding a dep from `from` to `to` creates a cycle. To figure + // that out, look for a path in the *opposite* direction, from `to` to + // `from`. + if let Some(path) = self.find_path(&mut FxHashSet::default(), dep.crate_id, from) { let path = path.into_iter().map(|it| (it, self[it].display_name.clone())).collect(); let err = CyclicDependenciesError { path }; - assert!(err.from().0 == from && err.to().0 == to); + assert!(err.from().0 == from && err.to().0 == dep.crate_id); return Err(err); } + + self.arena[from].add_dep(dep); Ok(()) } @@ -531,22 +491,15 @@ impl CrateGraph { .for_each(|(_, data)| data.dependencies.sort_by_key(|dep| dep.crate_id)); } - /// Extends this crate graph by adding a complete disjoint second crate + /// Extends this crate graph by adding a complete second crate /// graph and adjust the ids in the [`ProcMacroPaths`] accordingly. /// - /// This will deduplicate the crates of the graph where possible. - /// Note that for deduplication to fully work, `self`'s crate dependencies must be sorted by crate id. - /// If the crate dependencies were sorted, the resulting graph from this `extend` call will also - /// have the crate dependencies sorted. - /// - /// Returns a mapping from `other`'s crate ids to the new crate ids in `self`. + /// Returns a map mapping `other`'s IDs to the new IDs in `self`. pub fn extend( &mut self, mut other: CrateGraph, proc_macros: &mut ProcMacroPaths, - merge: impl Fn((CrateId, &mut CrateData), (CrateId, &CrateData)) -> bool, ) -> FxHashMap { - let m = self.len(); let topo = other.crates_in_topological_order(); let mut id_map: FxHashMap = FxHashMap::default(); for topo in topo { @@ -554,20 +507,13 @@ impl CrateGraph { crate_data.dependencies.iter_mut().for_each(|dep| dep.crate_id = id_map[&dep.crate_id]); crate_data.dependencies.sort_by_key(|dep| dep.crate_id); - let res = self - .arena - .iter_mut() - .take(m) - .find_map(|(id, data)| merge((id, data), (topo, crate_data)).then_some(id)); - let new_id = - if let Some(res) = res { res } else { self.arena.alloc(crate_data.clone()) }; + let new_id = self.arena.alloc(crate_data.clone()); id_map.insert(topo, new_id); } *proc_macros = mem::take(proc_macros).into_iter().map(|(id, macros)| (id_map[&id], macros)).collect(); - id_map } diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs index 20ef45d0b372..46e258d46f5c 100644 --- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs @@ -5,11 +5,12 @@ mod input; use std::panic; +use rustc_hash::FxHashMap; use salsa::Durability; use span::EditionedFileId; use syntax::{ast, Parse, SourceFile, SyntaxError}; use triomphe::Arc; -use vfs::FileId; +use vfs::{AbsPathBuf, FileId}; pub use crate::{ change::FileChange, @@ -74,19 +75,30 @@ pub trait SourceDatabase: FileLoader + std::fmt::Debug { #[salsa::input] fn crate_graph(&self) -> Arc; - // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query #[salsa::input] - fn data_layout(&self, krate: CrateId) -> TargetLayoutLoadResult; - - #[salsa::input] - fn toolchain(&self, krate: CrateId) -> Option; + fn crate_workspace_data(&self) -> Arc>>; #[salsa::transparent] fn toolchain_channel(&self, krate: CrateId) -> Option; } +/// Crate related data shared by the whole workspace. +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +pub struct CrateWorkspaceData { + /// The working directory to run proc-macros in. This is usually the workspace root of cargo workspaces. + pub proc_macro_cwd: Option, + // FIXME: Consider removing this, making HirDatabase::target_data_layout an input query + pub data_layout: TargetLayoutLoadResult, + /// Toolchain version used to compile the crate. + pub toolchain: Option, +} + fn toolchain_channel(db: &dyn SourceDatabase, krate: CrateId) -> Option { - db.toolchain(krate).as_ref().and_then(|v| ReleaseChannel::from_str(&v.pre)) + db.crate_workspace_data() + .get(&krate)? + .toolchain + .as_ref() + .and_then(|v| ReleaseChannel::from_str(&v.pre)) } fn parse(db: &dyn SourceDatabase, file_id: EditionedFileId) -> Parse { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/body.rs index dde1f142ab0c..9535b5aea7c7 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body.rs @@ -100,7 +100,14 @@ pub struct BodySourceMap { field_map_back: FxHashMap, pat_field_map_back: FxHashMap, - format_args_template_map: FxHashMap>, + template_map: Option< + Box<( + // format_args! + FxHashMap>, + // asm! + FxHashMap>>, + )>, + >, expansions: FxHashMap>, MacroFileId>, @@ -220,6 +227,17 @@ impl Body { pretty::print_expr_hir(db, self, owner, expr, edition) } + pub fn pretty_print_pat( + &self, + db: &dyn DefDatabase, + owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, + ) -> String { + pretty::print_pat_hir(db, self, owner, pat, oneline, edition) + } + fn new( db: &dyn DefDatabase, owner: DefWithBodyId, @@ -426,7 +444,16 @@ impl BodySourceMap { node: InFile<&ast::FormatArgsExpr>, ) -> Option<&[(syntax::TextRange, Name)]> { let src = node.map(AstPtr::new).map(AstPtr::upcast::); - self.format_args_template_map.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + self.template_map.as_ref()?.0.get(self.expr_map.get(&src)?).map(std::ops::Deref::deref) + } + + pub fn asm_template_args( + &self, + node: InFile<&ast::AsmExpr>, + ) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> { + let src = node.map(AstPtr::new).map(AstPtr::upcast::); + let expr = self.expr_map.get(&src)?; + Some(*expr).zip(self.template_map.as_ref()?.1.get(expr).map(std::ops::Deref::deref)) } /// Get a reference to the body source map's diagnostics. @@ -446,11 +473,14 @@ impl BodySourceMap { field_map_back, pat_field_map_back, expansions, - format_args_template_map, + template_map, diagnostics, binding_definitions, } = self; - format_args_template_map.shrink_to_fit(); + if let Some(template_map) = template_map { + template_map.0.shrink_to_fit(); + template_map.1.shrink_to_fit(); + } expr_map.shrink_to_fit(); expr_map_back.shrink_to_fit(); pat_map.shrink_to_fit(); @@ -463,4 +493,13 @@ impl BodySourceMap { diagnostics.shrink_to_fit(); binding_definitions.shrink_to_fit(); } + + pub fn template_map( + &self, + ) -> Option<&( + FxHashMap, Vec<(tt::TextRange, Name)>>, + FxHashMap, Vec>>, + )> { + self.template_map.as_deref() + } } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs index abf789582920..9c547574ecb1 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower.rs @@ -1,6 +1,8 @@ //! Transforms `ast::Expr` into an equivalent `hir_def::expr::Expr` //! representation. +mod asm; + use std::mem; use base_db::CrateId; @@ -35,8 +37,8 @@ use crate::{ FormatPlaceholder, FormatSign, FormatTrait, }, Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind, - Expr, ExprId, InlineAsm, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, - OffsetOf, Pat, PatId, RecordFieldPat, RecordLitField, Statement, + Expr, ExprId, Label, LabelId, Literal, LiteralOrConst, MatchArm, Movability, OffsetOf, Pat, + PatId, RecordFieldPat, RecordLitField, Statement, }, item_scope::BuiltinShadowMode, lang_item::LangItem, @@ -693,10 +695,7 @@ impl ExprCollector<'_> { } } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), - ast::Expr::AsmExpr(e) => { - let e = self.collect_expr_opt(e.expr()); - self.alloc_expr(Expr::InlineAsm(InlineAsm { e }), syntax_ptr) - } + ast::Expr::AsmExpr(e) => self.lower_inline_asm(e, syntax_ptr), ast::Expr::OffsetOfExpr(e) => { let container = Interned::new(TypeRef::from_ast_opt(&self.ctx(), e.ty())); let fields = e.fields().map(|it| it.as_name()).collect(); @@ -737,7 +736,7 @@ impl ExprCollector<'_> { /// `try { ; }` into `': { ; ::std::ops::Try::from_output(()) }` /// and save the `` to use it as a break target for desugaring of the `?` operator. fn desugar_try_block(&mut self, e: BlockExpr) -> ExprId { - let Some(try_from_output) = LangItem::TryTraitFromOutput.path(self.db, self.krate) else { + let Some(try_from_output) = self.lang_path(LangItem::TryTraitFromOutput) else { return self.collect_block(e); }; let label = self @@ -840,10 +839,10 @@ impl ExprCollector<'_> { fn collect_for_loop(&mut self, syntax_ptr: AstPtr, e: ast::ForExpr) -> ExprId { let Some((into_iter_fn, iter_next_fn, option_some, option_none)) = (|| { Some(( - LangItem::IntoIterIntoIter.path(self.db, self.krate)?, - LangItem::IteratorNext.path(self.db, self.krate)?, - LangItem::OptionSome.path(self.db, self.krate)?, - LangItem::OptionNone.path(self.db, self.krate)?, + self.lang_path(LangItem::IntoIterIntoIter)?, + self.lang_path(LangItem::IteratorNext)?, + self.lang_path(LangItem::OptionSome)?, + self.lang_path(LangItem::OptionNone)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -896,6 +895,15 @@ impl ExprCollector<'_> { Expr::Match { expr: iter_next_expr, arms: Box::new([none_arm, some_arm]) }, syntax_ptr, ); + let loop_inner = self.alloc_expr( + Expr::Block { + id: None, + statements: Box::default(), + tail: Some(loop_inner), + label: None, + }, + syntax_ptr, + ); let loop_outer = self.alloc_expr(Expr::Loop { body: loop_inner, label }, syntax_ptr); let iter_binding = self.alloc_binding(iter_name, BindingAnnotation::Mutable); let iter_pat = self.alloc_pat_desugared(Pat::Bind { id: iter_binding, subpat: None }); @@ -923,10 +931,10 @@ impl ExprCollector<'_> { fn collect_try_operator(&mut self, syntax_ptr: AstPtr, e: ast::TryExpr) -> ExprId { let Some((try_branch, cf_continue, cf_break, try_from_residual)) = (|| { Some(( - LangItem::TryTraitBranch.path(self.db, self.krate)?, - LangItem::ControlFlowContinue.path(self.db, self.krate)?, - LangItem::ControlFlowBreak.path(self.db, self.krate)?, - LangItem::TryTraitFromResidual.path(self.db, self.krate)?, + self.lang_path(LangItem::TryTraitBranch)?, + self.lang_path(LangItem::ControlFlowContinue)?, + self.lang_path(LangItem::ControlFlowBreak)?, + self.lang_path(LangItem::TryTraitFromResidual)?, )) })() else { // Some of the needed lang items are missing, so we can't desugar @@ -1839,7 +1847,7 @@ impl ExprCollector<'_> { }, syntax_ptr, ); - self.source_map.format_args_template_map.insert(idx, mappings); + self.source_map.template_map.get_or_insert_with(Default::default).0.insert(idx, mappings); idx } @@ -2052,7 +2060,12 @@ impl ExprCollector<'_> { is_assignee_expr: false, }) } + // endregion: format + + fn lang_path(&self, lang: LangItem) -> Option { + lang.path(self.db, self.krate) + } } fn pat_literal_to_hir(lit: &ast::LiteralPat) -> Option<(Literal, ast::Literal)> { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs new file mode 100644 index 000000000000..4213370ac195 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/lower/asm.rs @@ -0,0 +1,276 @@ +//! Lowering of inline assembly. +use hir_expand::name::Name; +use intern::Symbol; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::{ + ast::{self, HasName, IsString}, + AstNode, AstPtr, AstToken, T, +}; +use tt::{TextRange, TextSize}; + +use crate::{ + body::lower::{ExprCollector, FxIndexSet}, + hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass}, +}; + +impl ExprCollector<'_> { + pub(super) fn lower_inline_asm( + &mut self, + asm: ast::AsmExpr, + syntax_ptr: AstPtr, + ) -> ExprId { + let mut clobber_abis = FxIndexSet::default(); + let mut operands = vec![]; + let mut options = AsmOptions::empty(); + + let mut named_pos: FxHashMap = Default::default(); + let mut named_args: FxHashMap = Default::default(); + let mut reg_args: FxHashSet = Default::default(); + for piece in asm.asm_pieces() { + let slot = operands.len(); + let mut lower_reg = |reg: Option| { + let reg = reg?; + if let Some(string) = reg.string_token() { + reg_args.insert(slot); + Some(InlineAsmRegOrRegClass::Reg(Symbol::intern(string.text()))) + } else { + reg.name_ref().map(|name_ref| { + InlineAsmRegOrRegClass::RegClass(Symbol::intern(&name_ref.text())) + }) + } + }; + + let op = match piece { + ast::AsmPiece::AsmClobberAbi(clobber_abi) => { + if let Some(abi_name) = clobber_abi.string_token() { + clobber_abis.insert(Symbol::intern(abi_name.text())); + } + continue; + } + ast::AsmPiece::AsmOptions(opt) => { + opt.asm_options().for_each(|opt| { + options |= match opt.syntax().first_token().map_or(T![$], |it| it.kind()) { + T![att_syntax] => AsmOptions::ATT_SYNTAX, + T![may_unwind] => AsmOptions::MAY_UNWIND, + T![nomem] => AsmOptions::NOMEM, + T![noreturn] => AsmOptions::NORETURN, + T![nostack] => AsmOptions::NOSTACK, + T![preserves_flags] => AsmOptions::PRESERVES_FLAGS, + T![pure] => AsmOptions::PURE, + T![raw] => AsmOptions::RAW, + T![readonly] => AsmOptions::READONLY, + _ => return, + } + }); + continue; + } + ast::AsmPiece::AsmOperandNamed(op) => { + let name = op.name().map(|name| Symbol::intern(&name.text())); + if let Some(name) = &name { + named_args.insert(name.clone(), slot); + named_pos.insert(slot, name.clone()); + } + let Some(op) = op.asm_operand() else { continue }; + ( + name.map(Name::new_symbol_root), + match op { + ast::AsmOperand::AsmRegOperand(op) => { + let Some(dir_spec) = op.asm_dir_spec() else { + continue; + }; + let Some(reg) = lower_reg(op.asm_reg_spec()) else { + continue; + }; + if dir_spec.in_token().is_some() { + let expr = self.collect_expr_opt( + op.asm_operand_expr().and_then(|it| it.in_expr()), + ); + AsmOperand::In { reg, expr } + } else if dir_spec.out_token().is_some() { + let expr = op + .asm_operand_expr() + .and_then(|it| it.in_expr()) + .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_))) + .map(|expr| self.collect_expr(expr)); + AsmOperand::Out { reg, expr, late: false } + } else if dir_spec.lateout_token().is_some() { + let expr = op + .asm_operand_expr() + .and_then(|it| it.in_expr()) + .filter(|it| !matches!(it, ast::Expr::UnderscoreExpr(_))) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::Out { reg, expr, late: true } + } else if dir_spec.inout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + match op_expr.fat_arrow_token().is_some() { + true => { + let out_expr = op_expr + .out_expr() + .filter(|it| { + !matches!(it, ast::Expr::UnderscoreExpr(_)) + }) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::SplitInOut { + reg, + in_expr, + out_expr, + late: false, + } + } + false => { + AsmOperand::InOut { reg, expr: in_expr, late: false } + } + } + } else if dir_spec.inlateout_token().is_some() { + let Some(op_expr) = op.asm_operand_expr() else { continue }; + let in_expr = self.collect_expr_opt(op_expr.in_expr()); + match op_expr.fat_arrow_token().is_some() { + true => { + let out_expr = op_expr + .out_expr() + .filter(|it| { + !matches!(it, ast::Expr::UnderscoreExpr(_)) + }) + .map(|expr| self.collect_expr(expr)); + + AsmOperand::SplitInOut { + reg, + in_expr, + out_expr, + late: true, + } + } + false => { + AsmOperand::InOut { reg, expr: in_expr, late: true } + } + } + } else { + continue; + } + } + ast::AsmOperand::AsmLabel(l) => { + AsmOperand::Label(self.collect_block_opt(l.block_expr())) + } + ast::AsmOperand::AsmConst(c) => { + AsmOperand::Const(self.collect_expr_opt(c.expr())) + } + ast::AsmOperand::AsmSym(s) => { + let Some(path) = + s.path().and_then(|p| self.expander.parse_path(self.db, p)) + else { + continue; + }; + AsmOperand::Sym(path) + } + }, + ) + } + }; + operands.push(op); + } + + let mut mappings = vec![]; + let mut curarg = 0; + if !options.contains(AsmOptions::RAW) { + // Don't treat raw asm as a format string. + asm.template() + .enumerate() + .filter_map(|(idx, it)| Some((idx, it.clone(), self.expand_macros_to_string(it)?))) + .for_each(|(idx, expr, (s, is_direct_literal))| { + mappings.resize_with(idx + 1, Vec::default); + let Ok(text) = s.value() else { + return; + }; + let mappings = &mut mappings[idx]; + let template_snippet = match expr { + ast::Expr::Literal(literal) => match literal.kind() { + ast::LiteralKind::String(s) => Some(s.text().to_owned()), + _ => None, + }, + _ => None, + }; + let str_style = match s.quote_offsets() { + Some(offsets) => { + let raw = usize::from(offsets.quotes.0.len()) - 1; + // subtract 1 for the `r` prefix + (raw != 0).then(|| raw - 1) + } + None => None, + }; + + let mut parser = rustc_parse_format::Parser::new( + &text, + str_style, + template_snippet, + false, + rustc_parse_format::ParseMode::InlineAsm, + ); + parser.curarg = curarg; + + let mut unverified_pieces = Vec::new(); + while let Some(piece) = parser.next() { + if !parser.errors.is_empty() { + break; + } else { + unverified_pieces.push(piece); + } + } + + curarg = parser.curarg; + + let to_span = |inner_span: rustc_parse_format::InnerSpan| { + is_direct_literal.then(|| { + TextRange::new( + inner_span.start.try_into().unwrap(), + inner_span.end.try_into().unwrap(), + ) - TextSize::from(str_style.map(|it| it + 1).unwrap_or(0) as u32 + 1) + }) + }; + for piece in unverified_pieces { + match piece { + rustc_parse_format::Piece::String(_) => {} + rustc_parse_format::Piece::NextArgument(arg) => { + // let span = arg_spans.next(); + + let (operand_idx, _name) = match arg.position { + rustc_parse_format::ArgumentIs(idx) + | rustc_parse_format::ArgumentImplicitlyIs(idx) => { + if idx >= operands.len() + || named_pos.contains_key(&idx) + || reg_args.contains(&idx) + { + (None, None) + } else { + (Some(idx), None) + } + } + rustc_parse_format::ArgumentNamed(name) => { + let name = Symbol::intern(name); + ( + named_args.get(&name).copied(), + Some(Name::new_symbol_root(name)), + ) + } + }; + + if let Some(operand_idx) = operand_idx { + if let Some(position_span) = to_span(arg.position_span) { + mappings.push((position_span, operand_idx)); + } + } + } + } + } + }) + }; + let idx = self.alloc_expr( + Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }), + syntax_ptr, + ); + self.source_map.template_map.get_or_insert_with(Default::default).1.insert(idx, mappings); + idx + } +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs index 55740a68acd6..37167fcb8155 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/pretty.rs @@ -16,6 +16,13 @@ use crate::{ use super::*; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub(super) enum LineFormat { + Oneline, + Newline, + Indentation, +} + pub(super) fn print_body_hir( db: &dyn DefDatabase, body: &Body, @@ -52,7 +59,14 @@ pub(super) fn print_body_hir( } }; - let mut p = Printer { db, body, buf: header, indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: header, + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; if let DefWithBodyId::FunctionId(it) = owner { p.buf.push('('); let function_data = &db.function_data(it); @@ -95,12 +109,38 @@ pub(super) fn print_expr_hir( expr: ExprId, edition: Edition, ) -> String { - let mut p = - Printer { db, body, buf: String::new(), indent_level: 0, needs_indent: false, edition }; + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: LineFormat::Newline, + edition, + }; p.print_expr(expr); p.buf } +pub(super) fn print_pat_hir( + db: &dyn DefDatabase, + body: &Body, + _owner: DefWithBodyId, + pat: PatId, + oneline: bool, + edition: Edition, +) -> String { + let mut p = Printer { + db, + body, + buf: String::new(), + indent_level: 0, + line_format: if oneline { LineFormat::Oneline } else { LineFormat::Newline }, + edition, + }; + p.print_pat(pat); + p.buf +} + macro_rules! w { ($dst:expr, $($arg:tt)*) => { { let _ = write!($dst, $($arg)*); } @@ -109,10 +149,10 @@ macro_rules! w { macro_rules! wln { ($dst:expr) => { - { let _ = writeln!($dst); } + { $dst.newline(); } }; ($dst:expr, $($arg:tt)*) => { - { let _ = writeln!($dst, $($arg)*); } + { let _ = w!($dst, $($arg)*); $dst.newline(); } }; } @@ -121,24 +161,30 @@ struct Printer<'a> { body: &'a Body, buf: String, indent_level: usize, - needs_indent: bool, + line_format: LineFormat, edition: Edition, } impl Write for Printer<'_> { fn write_str(&mut self, s: &str) -> fmt::Result { for line in s.split_inclusive('\n') { - if self.needs_indent { + if matches!(self.line_format, LineFormat::Indentation) { match self.buf.chars().rev().find(|ch| *ch != ' ') { Some('\n') | None => {} _ => self.buf.push('\n'), } self.buf.push_str(&" ".repeat(self.indent_level)); - self.needs_indent = false; } self.buf.push_str(line); - self.needs_indent = line.ends_with('\n'); + + if matches!(self.line_format, LineFormat::Newline | LineFormat::Indentation) { + self.line_format = if line.ends_with('\n') { + LineFormat::Indentation + } else { + LineFormat::Newline + }; + } } Ok(()) @@ -161,14 +207,28 @@ impl Printer<'_> { } } + // Add a newline if the current line is not empty. + // If the current line is empty, add a space instead. + // + // Do not use [`writeln!()`] or [`wln!()`] here, which will result in + // infinite recursive calls to this function. fn newline(&mut self) { - match self.buf.chars().rev().find_position(|ch| *ch != ' ') { - Some((_, '\n')) | None => {} - Some((idx, _)) => { - if idx != 0 { - self.buf.drain(self.buf.len() - idx..); + if matches!(self.line_format, LineFormat::Oneline) { + match self.buf.chars().last() { + Some(' ') | None => {} + Some(_) => { + w!(self, " "); + } + } + } else { + match self.buf.chars().rev().find_position(|ch| *ch != ' ') { + Some((_, '\n')) | None => {} + Some((idx, _)) => { + if idx != 0 { + self.buf.drain(self.buf.len() - idx..); + } + w!(self, "\n"); } - writeln!(self).unwrap() } } } @@ -539,12 +599,14 @@ impl Printer<'_> { w!(self, ")"); } Pat::Or(pats) => { + w!(self, "("); for (i, pat) in pats.iter().enumerate() { if i != 0 { w!(self, " | "); } self.print_pat(*pat); } + w!(self, ")"); } Pat::Record { path, args, ellipsis } => { match path { @@ -554,12 +616,37 @@ impl Printer<'_> { w!(self, " {{"); let edition = self.edition; + let oneline = matches!(self.line_format, LineFormat::Oneline); self.indented(|p| { - for arg in args.iter() { - w!(p, "{}: ", arg.name.display(self.db.upcast(), edition)); - p.print_pat(arg.pat); - wln!(p, ","); + for (idx, arg) in args.iter().enumerate() { + let field_name = arg.name.display(self.db.upcast(), edition).to_string(); + + let mut same_name = false; + if let Pat::Bind { id, subpat: None } = &self.body[arg.pat] { + if let Binding { name, mode: BindingAnnotation::Unannotated, .. } = + &self.body.bindings[*id] + { + if name.as_str() == field_name { + same_name = true; + } + } + } + + w!(p, "{}", field_name); + + if !same_name { + w!(p, ": "); + p.print_pat(arg.pat); + } + + // Do not print the extra comma if the line format is oneline + if oneline && idx == args.len() - 1 { + w!(p, " "); + } else { + wln!(p, ","); + } } + if *ellipsis { wln!(p, ".."); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs index 38fc01d8fca5..dd3e79c874d8 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/body/tests.rs @@ -142,6 +142,41 @@ mod m { ); } +#[test] +fn desugar_for_loop() { + let (db, body, def) = lower( + r#" +//- minicore: iterator +fn main() { + for ident in 0..10 { + foo(); + bar() + } +} +"#, + ); + + expect![[r#" + fn main() -> () { + match builtin#lang(into_iter)( + (0) ..(10) , + ) { + mut 11 => loop { + match builtin#lang(next)( + &mut 11, + ) { + builtin#lang(None) => break, + builtin#lang(Some)(ident) => { + foo(); + bar() + }, + } + }, + } + }"#]] + .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT)) +} + #[test] fn desugar_builtin_format_args() { let (db, body, def) = lower( diff --git a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs index a70710e565c2..ba54451e594f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/data/adt.rs @@ -14,6 +14,7 @@ use triomphe::Arc; use crate::{ builtin_type::{BuiltinInt, BuiltinUint}, db::DefDatabase, + hir::Expr, item_tree::{ AttrOwner, Field, FieldParent, FieldsShape, ItemTree, ModItem, RawVisibilityId, TreeId, }, @@ -317,6 +318,27 @@ impl EnumData { _ => IntegerType::Pointer(true), } } + + // [Adopted from rustc](https://github.com/rust-lang/rust/blob/bd53aa3bf7a24a70d763182303bd75e5fc51a9af/compiler/rustc_middle/src/ty/adt.rs#L446-L448) + pub fn is_payload_free(&self, db: &dyn DefDatabase) -> bool { + self.variants.iter().all(|(v, _)| { + // The condition check order is slightly modified from rustc + // to improve performance by early returning with relatively fast checks + let variant = &db.enum_variant_data(*v).variant_data; + if !variant.fields().is_empty() { + return false; + } + // The outer if condition is whether this variant has const ctor or not + if !matches!(variant.kind(), StructKind::Unit) { + let body = db.body((*v).into()); + // A variant with explicit discriminant + if body.exprs[body.body_expr] != Expr::Missing { + return false; + } + } + true + }) + } } impl EnumVariantData { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs index 86fd092603ff..d9358a28822e 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs @@ -307,7 +307,120 @@ pub struct OffsetOf { #[derive(Debug, Clone, PartialEq, Eq)] pub struct InlineAsm { - pub e: ExprId, + pub operands: Box<[(Option, AsmOperand)]>, + pub options: AsmOptions, +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct AsmOptions(u16); +bitflags::bitflags! { + impl AsmOptions: u16 { + const PURE = 1 << 0; + const NOMEM = 1 << 1; + const READONLY = 1 << 2; + const PRESERVES_FLAGS = 1 << 3; + const NORETURN = 1 << 4; + const NOSTACK = 1 << 5; + const ATT_SYNTAX = 1 << 6; + const RAW = 1 << 7; + const MAY_UNWIND = 1 << 8; + } +} + +impl AsmOptions { + pub const COUNT: usize = Self::all().bits().count_ones() as usize; + + pub const GLOBAL_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW); + pub const NAKED_OPTIONS: Self = Self::ATT_SYNTAX.union(Self::RAW).union(Self::NORETURN); + + pub fn human_readable_names(&self) -> Vec<&'static str> { + let mut options = vec![]; + + if self.contains(AsmOptions::PURE) { + options.push("pure"); + } + if self.contains(AsmOptions::NOMEM) { + options.push("nomem"); + } + if self.contains(AsmOptions::READONLY) { + options.push("readonly"); + } + if self.contains(AsmOptions::PRESERVES_FLAGS) { + options.push("preserves_flags"); + } + if self.contains(AsmOptions::NORETURN) { + options.push("noreturn"); + } + if self.contains(AsmOptions::NOSTACK) { + options.push("nostack"); + } + if self.contains(AsmOptions::ATT_SYNTAX) { + options.push("att_syntax"); + } + if self.contains(AsmOptions::RAW) { + options.push("raw"); + } + if self.contains(AsmOptions::MAY_UNWIND) { + options.push("may_unwind"); + } + + options + } +} + +impl std::fmt::Debug for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + bitflags::parser::to_writer(self, f) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum AsmOperand { + In { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + }, + Out { + reg: InlineAsmRegOrRegClass, + expr: Option, + late: bool, + }, + InOut { + reg: InlineAsmRegOrRegClass, + expr: ExprId, + late: bool, + }, + SplitInOut { + reg: InlineAsmRegOrRegClass, + in_expr: ExprId, + out_expr: Option, + late: bool, + }, + Label(ExprId), + Const(ExprId), + Sym(Path), +} + +impl AsmOperand { + pub fn reg(&self) -> Option<&InlineAsmRegOrRegClass> { + match self { + Self::In { reg, .. } + | Self::Out { reg, .. } + | Self::InOut { reg, .. } + | Self::SplitInOut { reg, .. } => Some(reg), + Self::Const { .. } | Self::Sym { .. } | Self::Label { .. } => None, + } + } + + pub fn is_clobber(&self) -> bool { + matches!(self, AsmOperand::Out { reg: InlineAsmRegOrRegClass::Reg(_), late: _, expr: None }) + } +} + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub enum InlineAsmRegOrRegClass { + Reg(Symbol), + RegClass(Symbol), } #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -372,7 +485,21 @@ impl Expr { match self { Expr::Missing => {} Expr::Path(_) | Expr::OffsetOf(_) => {} - Expr::InlineAsm(it) => f(it.e), + Expr::InlineAsm(it) => it.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => f(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + f(*in_expr); + if let Some(out_expr) = out_expr { + f(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { f(*condition); f(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b6dbba12cd6d..a3b48831a0b0 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs @@ -50,11 +50,7 @@ fn main() { let i: u64 = 3; let o: u64; unsafe { - builtin #asm ( { - $crate::format_args!("mov {0}, {1}"); - $crate::format_args!("add {0}, 5"); - } - ); + builtin #asm ("mov {0}, {1}", "add {0}, 5", out (reg)o, in (reg)i, ); } } "##]], @@ -532,3 +528,21 @@ fn main() { foobar; } "##]], ); } + +#[test] +fn test_quote_string() { + check( + r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { stringify!("hello"); } +"##, + expect![[r##" +#[rustc_builtin_macro] +macro_rules! stringify {} + +fn main() { "\"hello\""; } +"##]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs index fc1460870cea..85fb90fdfb69 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -389,7 +389,7 @@ m! { foo# bar } m! { Foo,# Bar } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident),*) => ($(mod $i {} )*); ($($i:ident)#*) => ($(fn $i() {} )*); @@ -404,27 +404,29 @@ fn bar() {} struct Foo; struct Bar; -"##]], +"#]], ); } #[test] fn test_match_group_pattern_with_multiple_defs() { + // FIXME: The pretty printer breaks by leaving whitespace here, +syntaxctxt is used to avoid that check( r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } +// +syntaxctxt m! { foo, bar } "#, expect![[r#" macro_rules! m { ($($i:ident),*) => ( impl Bar { $(fn $i() {})* } ); } -impl Bar { - fn foo() {} - fn bar() {} -} +impl#\1# Bar#\1# {#\1# + fn#\1# foo#\0#(#\1#)#\1# {#\1#}#\1# + fn#\1# bar#\0#(#\1#)#\1# {#\1#}#\1# +}#\1# "#]], ); } @@ -480,12 +482,12 @@ macro_rules! m { } m!{#abc} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($i:ident)* #abc) => ( fn baz() { $($i ();)* } ); } fn baz() {} -"##]], +"#]], ) } @@ -1189,13 +1191,13 @@ macro_rules! m { m! { cfg(target_os = "windows") } m! { hello::world } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($m:meta) => ( #[$m] fn bar() {} ) } #[cfg(target_os = "windows")] fn bar() {} #[hello::world] fn bar() {} -"##]], +"#]], ); } @@ -1213,7 +1215,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1221,7 +1223,7 @@ macro_rules! m { #[doc = r" MultiLines Doc "] fn bar() {} -"##]], +"#]], ); } @@ -1234,12 +1236,12 @@ macro_rules! m { } m! { #[doc = concat!("The `", "bla", "` lang item.")] } "#, - expect![[r##" + expect![[r#" macro_rules! m { (#[$m:meta]) => ( #[$m] fn bar() {} ) } #[doc = concat!("The `", "bla", "` lang item.")] fn bar() {} -"##]], +"#]], ); } @@ -1257,7 +1259,7 @@ m! { */ } "#, - expect![[r##" + expect![[r#" macro_rules! m { ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) } @@ -1265,7 +1267,7 @@ macro_rules! m { #[doc = r" 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 "] fn bar() {} -"##]], +"#]], ); } @@ -1342,10 +1344,10 @@ fn test_tt_composite2() { macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } m! {#} "#, - expect![[r##" + expect![[r#" macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } abs!( = > #); -"##]], +"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs index bf7011983876..1430e2a9a994 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs @@ -311,3 +311,150 @@ fn test() { "#]], ); } + +#[test] +fn concat() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $a:ident, $b:literal ) => { + let ${concat($a, _, "123", _foo, $b, _, 123)}; + }; +} + +fn test() { + m!( abc, 456 ); + m!( def, "hello" ); +} +"#, + expect![[r#" +macro_rules! m { + ( $a:ident, $b:literal ) => { + let ${concat($a, _, "123", _foo, $b, _, 123)}; + }; +} + +fn test() { + let abc_123_foo456_123;; + let def_123_foohello_123;; +} +"#]], + ); +} + +#[test] +fn concat_less_than_two_elements() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + () => { + let ${concat(abc)}; + }; +} + +fn test() { + m!() +} +"#, + expect![[r#" +macro_rules! m { + () => { + let ${concat(abc)}; + }; +} + +fn test() { + /* error: macro definition has parse errors */ +} +"#]], + ); +} + +#[test] +fn concat_invalid_ident() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + () => { + let ${concat(abc, '"')}; + }; +} + +fn test() { + m!() +} +"#, + expect![[r#" +macro_rules! m { + () => { + let ${concat(abc, '"')}; + }; +} + +fn test() { + /* error: `${concat(..)}` is not generating a valid identifier */let __ra_concat_dummy; +} +"#]], + ); +} + +#[test] +fn concat_invalid_fragment() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $e:expr ) => { + let ${concat(abc, $e)}; + }; +} + +fn test() { + m!(()) +} +"#, + expect![[r#" +macro_rules! m { + ( $e:expr ) => { + let ${concat(abc, $e)}; + }; +} + +fn test() { + /* error: metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt` */let abc; +} +"#]], + ); +} + +#[test] +fn concat_repetition() { + // FIXME: Should this error? rustc currently accepts it. + check( + r#" +macro_rules! m { + ( $($i:ident)* ) => { + let ${concat(abc, $i)}; + }; +} + +fn test() { + m!(a b c) +} +"#, + expect![[r#" +macro_rules! m { + ( $($i:ident)* ) => { + let ${concat(abc, $i)}; + }; +} + +fn test() { + /* error: expected simple binding, found nested binding `i` */let abc; +} +"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 485f72e92ce1..1bbed01443de 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -139,7 +139,7 @@ STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}} STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}} "#, - expect![[r##" + expect![[r#" macro_rules! STRUCT { ($(#[$attrs:meta])* struct $name:ident { $($field:ident: $ftype:ty,)+ @@ -194,7 +194,7 @@ impl Clone for D3DCONTENTPROTECTIONCAPS { } } } -"##]], +"#]], ); } @@ -214,7 +214,7 @@ macro_rules! int_base { } int_base!{Binary for isize as usize -> Binary} "#, - expect![[r##" + expect![[r#" macro_rules! int_base { ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { #[stable(feature = "rust1", since = "1.0.0")] @@ -230,7 +230,7 @@ macro_rules! int_base { Binary.fmt_int(*self as usize, f) } } -"##]], +"#]], ); } @@ -318,7 +318,7 @@ impl_fn_for_zst ! { } "#, - expect![[r##" + expect![[r#" macro_rules! impl_fn_for_zst { {$( $( #[$attr: meta] )* struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = @@ -410,7 +410,7 @@ impl FnOnce<(char, )> for CharEscapeDefault { } } -"##]], +"#]], ); } @@ -511,7 +511,7 @@ cfg_if! { @__apply cfg(all(not(any(not(any(target_os = "solaris", target_os = "illumos")))))), } "#, - expect![[r##" + expect![[r#" macro_rules! cfg_if { ($(if #[cfg($($meta:meta),*)] { $($it:item)* } )else* else { $($it2:item)* }) => { @@ -534,7 +534,7 @@ __cfg_if_items! { } -"##]], +"#]], ); } @@ -618,7 +618,7 @@ RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID fn GetDataSize(&mut self) -> UINT }} "#, - expect![[r##" + expect![[r#" #[macro_export] macro_rules! RIDL { (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) @@ -639,7 +639,7 @@ impl ID3D11Asynchronous { ((*self .lpVtbl).GetDataSize)(self ) } } -"##]], +"#]], ); } @@ -676,7 +676,7 @@ quick_error ! ( ); "#, - expect![[r##" + expect![[r#" macro_rules! quick_error { (SORT [enum $name:ident $( #[$meta:meta] )*] items [$($( #[$imeta:meta] )* @@ -697,7 +697,7 @@ macro_rules! quick_error { } quick_error!(ENUMINITION[enum Wrapped#[derive(Debug)]]body[]queue[ = > One: UNIT[] = > Two: TUPLE[s: String]]); -"##]], +"#]], ) } @@ -746,7 +746,7 @@ delegate_impl ! { [G, &'a mut G, deref] pub trait Data: GraphBase {@section type type NodeWeight;} } "#, - expect![[r##" + expect![[r#" macro_rules! delegate_impl { ([$self_type:ident, $self_wrap:ty, $self_map:ident] pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { @@ -785,7 +785,7 @@ macro_rules! delegate_impl { } } impl <> Data for &'amut G where G: Data {} -"##]], +"#]], ); } @@ -959,14 +959,14 @@ macro_rules! with_std { with_std! {mod m;mod f;} "#, - expect![[r##" + expect![[r#" macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } #[cfg(feature = "std")] mod m; #[cfg(feature = "std")] mod f; -"##]], +"#]], ) } @@ -1144,3 +1144,27 @@ mod any { "#]], ); } + +#[test] +fn regression_18148() { + check( + r#" +macro_rules! m { + ( $e:expr ) => {}; +} + +fn foo() { + m!(r#const); +} +"#, + expect![[r#" +macro_rules! m { + ( $e:expr ) => {}; +} + +fn foo() { + ; +} +"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs index 7f761192517c..450a15bd66e5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs @@ -1,6 +1,6 @@ -//! This module contains tests for macro expansion. Effectively, it covers `tt`, -//! `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a -//! wrong architecture at the first glance, but is intentional. +//! This module contains integration tests for macro expansion with name resolution. Effectively, it +//! covers `tt`, `mbe`, `proc_macro_api` and `hir_expand` crates. This might seem like a wrong +//! architecture at the first glance, but is intentional. //! //! Physically, macro expansion process is intertwined with name resolution. You //! can not expand *just* the syntax. So, to be able to write integration tests @@ -320,6 +320,7 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result { let (parse, _) = syntax_bridge::token_tree_to_syntax_node( subtree, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 6f605c0cb33d..c0178adc9a63 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -16,12 +16,12 @@ fn attribute_macro_attr_censoring() { #[attr1] #[proc_macros::identity] #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[proc_macros::identity] #[attr2] struct S; #[attr1] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -39,7 +39,7 @@ fn derive_censoring() { #[attr2] struct S; "#, - expect![[r##" + expect![[r#" #[attr1] #[derive(Foo)] #[derive(proc_macros::DeriveIdentity)] @@ -49,7 +49,7 @@ struct S; #[attr1] #[derive(Bar)] -#[attr2] struct S;"##]], +#[attr2] struct S;"#]], ); } @@ -62,14 +62,14 @@ fn attribute_macro_syntax_completion_1() { #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } "#, - expect![[r##" + expect![[r#" #[proc_macros::identity_when_valid] fn foo() { bar.baz(); blub } fn foo() { bar.baz(); blub -}"##]], +}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs index 11601c683e1e..9bd7d38f0a64 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -69,7 +69,7 @@ use la_arena::Arena; use rustc_hash::{FxHashMap, FxHashSet}; use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID}; use stdx::format_to; -use syntax::{ast, SmolStr}; +use syntax::{ast, AstNode, SmolStr, SyntaxNode}; use triomphe::Arc; use tt::TextRange; @@ -291,7 +291,7 @@ impl ModuleOrigin { /// Returns a node which defines this module. /// That is, a file or a `mod foo {}` with items. - fn definition_source(&self, db: &dyn DefDatabase) -> InFile { + pub fn definition_source(&self, db: &dyn DefDatabase) -> InFile { match self { &ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => { let sf = db.parse(definition).tree(); @@ -728,6 +728,16 @@ pub enum ModuleSource { BlockExpr(ast::BlockExpr), } +impl ModuleSource { + pub fn node(&self) -> SyntaxNode { + match self { + ModuleSource::SourceFile(it) => it.syntax().clone(), + ModuleSource::Module(it) => it.syntax().clone(), + ModuleSource::BlockExpr(it) => it.syntax().clone(), + } + } +} + /// See `sub_namespace_match()`. #[derive(Clone, Copy, PartialEq, Eq)] pub enum MacroSubNs { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs index 96db3db8f0d8..e09ef4f205d3 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs @@ -221,7 +221,7 @@ struct DefCollector<'a> { deps: FxHashMap, glob_imports: FxHashMap>, unresolved_imports: Vec, - indeterminate_imports: Vec, + indeterminate_imports: Vec<(ImportDirective, PerNs)>, unresolved_macros: Vec, mod_dirs: FxHashMap, cfg_options: &'a CfgOptions, @@ -415,16 +415,6 @@ impl DefCollector<'_> { self.resolution_loop(); - // Resolve all indeterminate resolved imports again - // As some of the macros will expand newly import shadowing partial resolved imports - // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports` - // correctly - let partial_resolved = self.indeterminate_imports.drain(..).map(|directive| { - ImportDirective { status: PartialResolvedImport::Unresolved, ..directive } - }); - self.unresolved_imports.extend(partial_resolved); - self.resolve_imports(); - let unresolved_imports = mem::take(&mut self.unresolved_imports); // show unresolved imports in completion, etc for directive in &unresolved_imports { @@ -749,9 +739,9 @@ impl DefCollector<'_> { .filter_map(|mut directive| { directive.status = self.resolve_import(directive.module_id, &directive.import); match directive.status { - PartialResolvedImport::Indeterminate(_) => { + PartialResolvedImport::Indeterminate(resolved) => { self.record_resolved_import(&directive); - self.indeterminate_imports.push(directive); + self.indeterminate_imports.push((directive, resolved)); res = ReachedFixedPoint::No; None } @@ -764,6 +754,33 @@ impl DefCollector<'_> { } }) .collect(); + + // Resolve all indeterminate resolved imports again + // As some of the macros will expand newly import shadowing partial resolved imports + // FIXME: We maybe could skip this, if we handle the indeterminate imports in `resolve_imports` + // correctly + let mut indeterminate_imports = std::mem::take(&mut self.indeterminate_imports); + indeterminate_imports.retain_mut(|(directive, partially_resolved)| { + let partially_resolved = partially_resolved.availability(); + directive.status = self.resolve_import(directive.module_id, &directive.import); + match directive.status { + PartialResolvedImport::Indeterminate(import) + if partially_resolved != import.availability() => + { + self.record_resolved_import(directive); + res = ReachedFixedPoint::No; + false + } + PartialResolvedImport::Resolved(_) => { + self.record_resolved_import(directive); + res = ReachedFixedPoint::No; + false + } + _ => true, + } + }); + self.indeterminate_imports = indeterminate_imports; + res } diff --git a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs index 19485c476f72..3f3b98c6b5b5 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/per_ns.rs @@ -3,6 +3,8 @@ //! //! `PerNs` (per namespace) captures this. +use bitflags::bitflags; + use crate::{ item_scope::{ImportId, ImportOrExternCrate, ItemInNs}, visibility::Visibility, @@ -16,6 +18,16 @@ pub enum Namespace { Macros, } +bitflags! { + /// Describes only the presence/absence of each namespace, without its value. + #[derive(Debug, PartialEq, Eq)] + pub(crate) struct NsAvailability : u32 { + const TYPES = 1 << 0; + const VALUES = 1 << 1; + const MACROS = 1 << 2; + } +} + #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] pub struct PerNs { pub types: Option<(ModuleDefId, Visibility, Option)>, @@ -24,6 +36,14 @@ pub struct PerNs { } impl PerNs { + pub(crate) fn availability(&self) -> NsAvailability { + let mut result = NsAvailability::empty(); + result.set(NsAvailability::TYPES, self.types.is_some()); + result.set(NsAvailability::VALUES, self.values.is_some()); + result.set(NsAvailability::MACROS, self.macros.is_some()); + result + } + pub fn none() -> PerNs { PerNs { types: None, values: None, macros: None } } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs index 252430e4e954..7b9b7f36e2cd 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin.rs @@ -1,6 +1,6 @@ //! Builtin macros and attributes #[macro_use] -mod quote; +pub mod quote; mod attr_macro; mod derive_macro; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs index 795d9b14df96..d04225b87227 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs @@ -119,9 +119,8 @@ register_builtin! { (module_path, ModulePath) => module_path_expand, (assert, Assert) => assert_expand, (stringify, Stringify) => stringify_expand, - (llvm_asm, LlvmAsm) => asm_expand, (asm, Asm) => asm_expand, - (global_asm, GlobalAsm) => global_asm_expand, + (global_asm, GlobalAsm) => asm_expand, (cfg, Cfg) => cfg_expand, (core_panic, CorePanic) => panic_expand, (std_panic, StdPanic) => panic_expand, @@ -324,40 +323,15 @@ fn asm_expand( tt: &tt::Subtree, span: Span, ) -> ExpandResult { - // We expand all assembly snippets to `format_args!` invocations to get format syntax - // highlighting for them. - let mut literals = Vec::new(); - for tt in tt.token_trees.chunks(2) { - match tt { - [tt::TokenTree::Leaf(tt::Leaf::Literal(lit))] - | [tt::TokenTree::Leaf(tt::Leaf::Literal(lit)), tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', span: _, spacing: _ }))] => - { - let dollar_krate = dollar_crate(span); - literals.push(quote!(span=>#dollar_krate::format_args!(#lit);)); - } - _ => break, - } - } - + let mut tt = tt.clone(); + tt.delimiter.kind = tt::DelimiterKind::Parenthesis; let pound = mk_pound(span); let expanded = quote! {span => - builtin #pound asm ( - {##literals} - ) + builtin #pound asm #tt }; ExpandResult::ok(expanded) } -fn global_asm_expand( - _db: &dyn ExpandDatabase, - _id: MacroCallId, - _tt: &tt::Subtree, - span: Span, -) -> ExpandResult { - // Expand to nothing (at item-level) - ExpandResult::ok(quote! {span =>}) -} - fn cfg_expand( db: &dyn ExpandDatabase, id: MacroCallId, @@ -509,7 +483,7 @@ fn concat_expand( match it.kind { tt::LitKind::Char => { if let Ok(c) = unescape_char(it.symbol.as_str()) { - text.extend(c.escape_default()); + text.push(c); } record_span(it.span); } @@ -517,11 +491,11 @@ fn concat_expand( format_to!(text, "{}", it.symbol.as_str()) } tt::LitKind::Str => { - text.push_str(it.symbol.as_str()); + text.push_str(unescape_str(&it.symbol).as_str()); record_span(it.span); } tt::LitKind::StrRaw(_) => { - format_to!(text, "{}", it.symbol.as_str().escape_debug()); + format_to!(text, "{}", it.symbol.as_str()); record_span(it.span); } tt::LitKind::Byte @@ -839,7 +813,7 @@ fn include_str_expand( fn get_env_inner(db: &dyn ExpandDatabase, arg_id: MacroCallId, key: &Symbol) -> Option { let krate = db.lookup_intern_macro_call(arg_id).krate; - db.crate_graph()[krate].env.get(key.as_str()).map(|it| it.escape_debug().to_string()) + db.crate_graph()[krate].env.get(key.as_str()) } fn env_expand( diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs index 5c33f817f9e4..418d8d9660b5 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs @@ -3,6 +3,7 @@ use intern::{sym, Symbol}; use span::Span; +use syntax::ToSmolStr; use tt::IdentIsRaw; use crate::name::Name; @@ -17,6 +18,7 @@ pub(crate) fn dollar_crate(span: Span) -> tt::Ident { // 2. #()* pattern repetition not supported now // * But we can do it manually, see `test_quote_derive_copy_hack` #[doc(hidden)] +#[macro_export] macro_rules! quote_impl__ { ($span:ident) => { Vec::<$crate::tt::TokenTree>::new() @@ -26,8 +28,8 @@ macro_rules! quote_impl__ { { let children = $crate::builtin::quote::__quote!($span $($tt)*); $crate::tt::Subtree { - delimiter: crate::tt::Delimiter { - kind: crate::tt::DelimiterKind::$delim, + delimiter: $crate::tt::Delimiter { + kind: $crate::tt::DelimiterKind::$delim, open: $span, close: $span, }, @@ -39,9 +41,9 @@ macro_rules! quote_impl__ { ( @PUNCT($span:ident) $first:literal ) => { { vec![ - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $first, - spacing: crate::tt::Spacing::Alone, + spacing: $crate::tt::Spacing::Alone, span: $span, }).into() ] @@ -51,14 +53,14 @@ macro_rules! quote_impl__ { ( @PUNCT($span:ident) $first:literal, $sec:literal ) => { { vec![ - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $first, - spacing: crate::tt::Spacing::Joint, + spacing: $crate::tt::Spacing::Joint, span: $span, }).into(), - crate::tt::Leaf::Punct(crate::tt::Punct { + $crate::tt::Leaf::Punct($crate::tt::Punct { char: $sec, - spacing: crate::tt::Spacing::Alone, + spacing: $crate::tt::Spacing::Alone, span: $span, }).into() ] @@ -97,7 +99,7 @@ macro_rules! quote_impl__ { // Ident ($span:ident $tt:ident ) => { vec![ { - crate::tt::Leaf::Ident(crate::tt::Ident { + $crate::tt::Leaf::Ident($crate::tt::Ident { sym: intern::Symbol::intern(stringify!($tt)), span: $span, is_raw: tt::IdentIsRaw::No, @@ -108,6 +110,7 @@ macro_rules! quote_impl__ { // Puncts // FIXME: Not all puncts are handled ($span:ident -> ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '-', '>')}; + ($span:ident => ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '=', '>')}; ($span:ident & ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '&')}; ($span:ident , ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ',')}; ($span:ident : ) => {$crate::builtin::quote::__quote!(@PUNCT($span) ':')}; @@ -117,6 +120,9 @@ macro_rules! quote_impl__ { ($span:ident < ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '<')}; ($span:ident > ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '>')}; ($span:ident ! ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '!')}; + ($span:ident # ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '#')}; + ($span:ident $ ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '$')}; + ($span:ident * ) => {$crate::builtin::quote::__quote!(@PUNCT($span) '*')}; ($span:ident $first:tt $($tail:tt)+ ) => { { @@ -128,18 +134,19 @@ macro_rules! quote_impl__ { } }; } -pub(super) use quote_impl__ as __quote; +pub use quote_impl__ as __quote; /// FIXME: /// It probably should implement in proc-macro -macro_rules! quote_impl { +#[macro_export] +macro_rules! quote { ($span:ident=> $($tt:tt)* ) => { $crate::builtin::quote::IntoTt::to_subtree($crate::builtin::quote::__quote!($span $($tt)*), $span) } } -pub(super) use quote_impl as quote; +pub(super) use quote; -pub(crate) trait IntoTt { +pub trait IntoTt { fn to_subtree(self, span: Span) -> crate::tt::Subtree; fn to_tokens(self) -> Vec; } @@ -167,7 +174,7 @@ impl IntoTt for crate::tt::Subtree { } } -pub(crate) trait ToTokenTree { +pub trait ToTokenTree { fn to_token(self, span: Span) -> crate::tt::TokenTree; } @@ -211,8 +218,8 @@ impl_to_to_tokentrees! { _span: crate::tt::Literal => self { self }; _span: crate::tt::Ident => self { self }; _span: crate::tt::Punct => self { self }; - span: &str => self { crate::tt::Literal{symbol: Symbol::intern(self), span, kind: tt::LitKind::Str, suffix: None }}; - span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self), span, kind: tt::LitKind::Str, suffix: None }}; + span: &str => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; + span: String => self { crate::tt::Literal{symbol: Symbol::intern(&self.escape_default().to_smolstr()), span, kind: tt::LitKind::Str, suffix: None }}; span: Name => self { let (is_raw, s) = IdentIsRaw::split_from_symbol(self.as_str()); crate::tt::Ident{sym: Symbol::intern(s), span, is_raw } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index 147cf912da1e..01a3103af82d 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -6,7 +6,7 @@ use cfg::{CfgAtom, CfgExpr}; use intern::{sym, Symbol}; use rustc_hash::FxHashSet; use syntax::{ - ast::{self, Attr, HasAttrs, Meta, VariantList}, + ast::{self, Attr, HasAttrs, Meta, TokenTree, VariantList}, AstNode, NodeOrToken, SyntaxElement, SyntaxKind, SyntaxNode, T, }; use tracing::{debug, warn}; @@ -17,7 +17,7 @@ fn check_cfg(db: &dyn ExpandDatabase, attr: &Attr, krate: CrateId) -> Option Optio if !attr.simple_name().as_deref().map(|v| v == "cfg_attr")? { return None; } - let cfg_expr = parse_from_attr_meta(attr.meta()?)?; + check_cfg_attr_value(db, &attr.token_tree()?, krate) +} + +pub fn check_cfg_attr_value( + db: &dyn ExpandDatabase, + attr: &TokenTree, + krate: CrateId, +) -> Option { + let cfg_expr = parse_from_attr_token_tree(attr)?; let enabled = db.crate_graph()[krate].cfg_options.check(&cfg_expr) != Some(false); Some(enabled) } @@ -238,8 +246,7 @@ pub(crate) fn process_cfg_attrs( Some(remove) } /// Parses a `cfg` attribute from the meta -fn parse_from_attr_meta(meta: Meta) -> Option { - let tt = meta.token_tree()?; +fn parse_from_attr_token_tree(tt: &TokenTree) -> Option { let mut iter = tt .token_trees_and_tokens() .filter(is_not_whitespace) @@ -328,7 +335,7 @@ mod tests { use expect_test::{expect, Expect}; use syntax::{ast::Attr, AstNode, SourceFile}; - use crate::cfg_process::parse_from_attr_meta; + use crate::cfg_process::parse_from_attr_token_tree; fn check_dnf_from_syntax(input: &str, expect: Expect) { let parse = SourceFile::parse(input, span::Edition::CURRENT); @@ -342,7 +349,7 @@ mod tests { let node = node.clone_subtree(); assert_eq!(node.syntax().text_range().start(), 0.into()); - let cfg = parse_from_attr_meta(node.meta().unwrap()).unwrap(); + let cfg = parse_from_attr_token_tree(&node.meta().unwrap().token_tree().unwrap()).unwrap(); let actual = format!("#![cfg({})]", DnfExpr::new(&cfg)); expect.assert_eq(&actual); } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs index 8b3f69db0277..de3a7b9f5615 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/change.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/change.rs @@ -1,10 +1,10 @@ //! Defines a unit of change that can applied to the database to get the next //! state. Changes are transactional. use base_db::{ - salsa::Durability, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootDatabase, - TargetLayoutLoadResult, Version, + salsa::Durability, CrateGraph, CrateId, CrateWorkspaceData, FileChange, SourceRoot, + SourceRootDatabase, }; -use la_arena::RawIdx; +use rustc_hash::FxHashMap; use span::FileId; use triomphe::Arc; @@ -14,8 +14,6 @@ use crate::{db::ExpandDatabase, proc_macro::ProcMacros}; pub struct ChangeWithProcMacros { pub source_change: FileChange, pub proc_macros: Option, - pub toolchains: Option>>, - pub target_data_layouts: Option>, } impl ChangeWithProcMacros { @@ -28,46 +26,25 @@ impl ChangeWithProcMacros { if let Some(proc_macros) = self.proc_macros { db.set_proc_macros_with_durability(Arc::new(proc_macros), Durability::HIGH); } - if let Some(target_data_layouts) = self.target_data_layouts { - for (id, val) in target_data_layouts.into_iter().enumerate() { - db.set_data_layout_with_durability( - CrateId::from_raw(RawIdx::from(id as u32)), - val, - Durability::HIGH, - ); - } - } - if let Some(toolchains) = self.toolchains { - for (id, val) in toolchains.into_iter().enumerate() { - db.set_toolchain_with_durability( - CrateId::from_raw(RawIdx::from(id as u32)), - val, - Durability::HIGH, - ); - } - } } pub fn change_file(&mut self, file_id: FileId, new_text: Option) { self.source_change.change_file(file_id, new_text) } - pub fn set_crate_graph(&mut self, graph: CrateGraph) { - self.source_change.set_crate_graph(graph) + pub fn set_crate_graph( + &mut self, + graph: CrateGraph, + ws_data: FxHashMap>, + ) { + self.source_change.set_crate_graph(graph); + self.source_change.set_ws_data(ws_data); } pub fn set_proc_macros(&mut self, proc_macros: ProcMacros) { self.proc_macros = Some(proc_macros); } - pub fn set_toolchains(&mut self, toolchains: Vec>) { - self.toolchains = Some(toolchains); - } - - pub fn set_target_data_layouts(&mut self, target_data_layouts: Vec) { - self.target_data_layouts = Some(target_data_layouts); - } - pub fn set_roots(&mut self, roots: Vec) { self.source_change.set_roots(roots) } diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index b1a6eed2fbc7..86dd4aef090f 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -1,4 +1,4 @@ -//! Compiled declarative macro expanders (`macro_rules!`` and `macro`) +//! Compiled declarative macro expanders (`macro_rules!` and `macro`) use base_db::CrateId; use intern::sym; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs index 19c3c9c43f10..95380979492a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs @@ -21,6 +21,7 @@ pub mod span_map; mod cfg_process; mod fixup; +mod prettify_macro_expansion_; use attrs::collect_attrs; use rustc_hash::FxHashMap; @@ -51,7 +52,11 @@ use crate::{ span_map::{ExpansionSpanMap, SpanMap}, }; -pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile}; +pub use crate::{ + cfg_process::check_cfg_attr_value, + files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile}, + prettify_macro_expansion_::prettify_macro_expansion, +}; pub use mbe::{DeclarativeMacro, ValueResult}; pub use span::{HirFileId, MacroCallId, MacroFileId}; diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs new file mode 100644 index 000000000000..d928cafdefc0 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs @@ -0,0 +1,60 @@ +//! Pretty printing of macros output. + +use base_db::CrateId; +use rustc_hash::FxHashMap; +use syntax::NodeOrToken; +use syntax::{ast::make, SyntaxNode}; + +use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap}; + +/// Inserts whitespace and replaces `$crate` in macro expansions. +#[expect(deprecated)] +pub fn prettify_macro_expansion( + db: &dyn ExpandDatabase, + syn: SyntaxNode, + span_map: &ExpansionSpanMap, + target_crate_id: CrateId, +) -> SyntaxNode { + let crate_graph = db.crate_graph(); + let target_crate = &crate_graph[target_crate_id]; + let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default(); + syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| { + let ctx = span_map.span_at(dollar_crate.text_range().start()).ctx; + let replacement = + syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| { + let ctx_data = db.lookup_intern_syntax_context(ctx); + let macro_call_id = + ctx_data.outer_expn.expect("`$crate` cannot come from `SyntaxContextId::ROOT`"); + let macro_call = db.lookup_intern_macro_call(macro_call_id); + let macro_def_crate = macro_call.def.krate; + // First, if this is the same crate as the macro, nothing will work but `crate`. + // If not, if the target trait has the macro's crate as a dependency, using the dependency name + // will work in inserted code and match the user's expectation. + // If not, the crate's display name is what the dependency name is likely to be once such dependency + // is inserted, and also understandable to the user. + // Lastly, if nothing else found, resort to leaving `$crate`. + if target_crate_id == macro_def_crate { + make::tokens::crate_kw() + } else if let Some(dep) = + target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate) + { + make::tokens::ident(&dep.name) + } else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name { + make::tokens::ident(crate_name.crate_name()) + } else { + return dollar_crate.clone(); + } + }); + if replacement.text() == "$crate" { + // The parent may have many children, and looking for the token may yield incorrect results. + return dollar_crate.clone(); + } + // We need to `clone_subtree()` but rowan doesn't provide such operation for tokens. + let parent = replacement.parent().unwrap().clone_subtree().clone_for_update(); + parent + .children_with_tokens() + .filter_map(NodeOrToken::into_token) + .find(|it| it.kind() == replacement.kind()) + .unwrap() + }) +} diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs index 26bb3a3edda4..fe09f0307c9e 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs @@ -29,6 +29,7 @@ pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result; } @@ -234,8 +235,18 @@ impl CustomProcMacroExpander { let krate_graph = db.crate_graph(); // Proc macros have access to the environment variables of the invoking crate. let env = &krate_graph[calling_crate].env; - match proc_macro.expander.expand(tt, attr_arg, env, def_site, call_site, mixed_site) - { + match proc_macro.expander.expand( + tt, + attr_arg, + env, + def_site, + call_site, + mixed_site, + db.crate_workspace_data()[&calling_crate] + .proc_macro_cwd + .as_ref() + .map(ToString::to_string), + ) { Ok(t) => ExpandResult::ok(t), Err(err) => match err { // Don't discard the item in case something unexpected happened while expanding attributes diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs index a3e4da5d1afd..e74e3d789883 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs @@ -381,9 +381,9 @@ impl chalk_solve::RustIrDatabase for ChalkContext<'_> { TyKind::Error.intern(Interner) } - fn is_object_safe(&self, _trait_id: chalk_ir::TraitId) -> bool { - // FIXME: implement actual object safety - true + fn is_object_safe(&self, trait_id: chalk_ir::TraitId) -> bool { + let trait_ = from_chalk_trait_id(trait_id); + crate::object_safety::object_safety(self.db, trait_).is_none() } fn closure_kind( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs index 8b6cde975f63..968a828e9dfe 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs @@ -11,7 +11,7 @@ use hir_def::{ ConstBlockLoc, EnumVariantId, GeneralConstId, StaticId, }; use hir_expand::Lookup; -use stdx::never; +use stdx::{never, IsNoneOr}; use triomphe::Arc; use crate::{ @@ -184,6 +184,22 @@ pub fn try_const_usize(db: &dyn HirDatabase, c: &Const) -> Option { } } +pub fn try_const_isize(db: &dyn HirDatabase, c: &Const) -> Option { + match &c.data(Interner).value { + chalk_ir::ConstValue::BoundVar(_) => None, + chalk_ir::ConstValue::InferenceVar(_) => None, + chalk_ir::ConstValue::Placeholder(_) => None, + chalk_ir::ConstValue::Concrete(c) => match &c.interned { + ConstScalar::Bytes(it, _) => Some(i128::from_le_bytes(pad16(it, true))), + ConstScalar::UnevaluatedConst(c, subst) => { + let ec = db.const_eval(*c, subst.clone(), None).ok()?; + try_const_isize(db, &ec) + } + _ => None, + }, + } +} + pub(crate) fn const_eval_recover( _: &dyn HirDatabase, _: &Cycle, @@ -256,8 +272,8 @@ pub(crate) fn const_eval_discriminant_variant( ) -> Result { let def = variant_id.into(); let body = db.body(def); + let loc = variant_id.lookup(db.upcast()); if body.exprs[body.body_expr] == Expr::Missing { - let loc = variant_id.lookup(db.upcast()); let prev_idx = loc.index.checked_sub(1); let value = match prev_idx { Some(prev_idx) => { @@ -269,13 +285,21 @@ pub(crate) fn const_eval_discriminant_variant( }; return Ok(value); } + + let repr = db.enum_data(loc.parent).repr; + let is_signed = IsNoneOr::is_none_or(repr.and_then(|repr| repr.int), |int| int.is_signed()); + let mir_body = db.monomorphized_mir_body( def, Substitution::empty(Interner), db.trait_environment_for_body(def), )?; let c = interpret_mir(db, mir_body, false, None).0?; - let c = try_const_usize(db, &c).unwrap() as i128; + let c = if is_signed { + try_const_isize(db, &c).unwrap() + } else { + try_const_usize(db, &c).unwrap() as i128 + }; Ok(c) } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs index 86228250c200..7093fcadcb03 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests.rs @@ -186,7 +186,13 @@ fn floating_point() { #[test] fn casts() { - check_number(r#"const GOAL: usize = 12 as *const i32 as usize"#, 12); + check_number( + r#" + //- minicore: sized + const GOAL: usize = 12 as *const i32 as usize + "#, + 12, + ); check_number( r#" //- minicore: coerce_unsized, index, slice @@ -204,7 +210,7 @@ fn casts() { r#" //- minicore: coerce_unsized, index, slice const GOAL: i16 = { - let a = &mut 5; + let a = &mut 5_i16; let z = a as *mut _; unsafe { *z } }; @@ -244,7 +250,13 @@ fn casts() { "#, 4, ); - check_number(r#"const GOAL: i32 = -12i8 as i32"#, -12); + check_number( + r#" + //- minicore: sized + const GOAL: i32 = -12i8 as i32 + "#, + -12, + ); } #[test] @@ -1544,7 +1556,7 @@ fn builtin_derive_macro() { Bar, } #[derive(Clone)] - struct X(i32, Z, i64) + struct X(i32, Z, i64); #[derive(Clone)] struct Y { field1: i32, @@ -1562,20 +1574,20 @@ fn builtin_derive_macro() { ); check_number( r#" - //- minicore: default, derive, builtin_impls - #[derive(Default)] - struct X(i32, Y, i64) - #[derive(Default)] - struct Y { - field1: i32, - field2: u8, - } +//- minicore: default, derive, builtin_impls +#[derive(Default)] +struct X(i32, Y, i64); +#[derive(Default)] +struct Y { + field1: i32, + field2: u8, +} - const GOAL: u8 = { - let x = X::default(); - x.1.field2 - }; - "#, +const GOAL: u8 = { + let x = X::default(); + x.1.field2 +}; +"#, 0, ); } @@ -1911,6 +1923,7 @@ fn function_pointer() { ); check_number( r#" + //- minicore: sized fn add2(x: u8) -> u8 { x + 2 } @@ -2007,7 +2020,7 @@ fn function_traits() { ); check_number( r#" - //- minicore: coerce_unsized, fn + //- minicore: coerce_unsized, fn, dispatch_from_dyn fn add2(x: u8) -> u8 { x + 2 } @@ -2062,7 +2075,7 @@ fn function_traits() { fn dyn_trait() { check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> u8 { 10 } } @@ -2085,7 +2098,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait Foo { fn foo(&self) -> i32 { 10 } } @@ -2109,7 +2122,7 @@ fn dyn_trait() { ); check_number( r#" - //- minicore: coerce_unsized, index, slice + //- minicore: coerce_unsized, index, slice, dispatch_from_dyn trait A { fn x(&self) -> i32; } @@ -2422,6 +2435,7 @@ fn statics() { fn extern_weak_statics() { check_number( r#" + //- minicore: sized extern "C" { #[linkage = "extern_weak"] static __dso_handle: *mut u8; @@ -2716,6 +2730,7 @@ fn const_trait_assoc() { ); check_number( r#" + //- minicore: sized struct S(*mut T); trait MySized: Sized { @@ -2813,7 +2828,7 @@ fn type_error() { y.0 }; "#, - |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::TypeMismatch(_))), + |e| matches!(e, ConstEvalError::MirLowerError(MirLowerError::HasErrors)), ); } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs index 5972b80d1696..c5706172b20c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval/tests/intrinsics.rs @@ -89,7 +89,7 @@ fn size_of_val() { ); check_number( r#" - //- minicore: coerce_unsized, fmt, builtin_impls + //- minicore: coerce_unsized, fmt, builtin_impls, dispatch_from_dyn extern "rust-intrinsic" { pub fn size_of_val(_: *const T) -> usize; } @@ -311,6 +311,7 @@ fn saturating() { fn allocator() { check_number( r#" + //- minicore: sized extern "Rust" { #[rustc_allocator] fn __rust_alloc(size: usize, align: usize) -> *mut u8; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs index 9a1f2158bf75..ce5a821ea2bc 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs @@ -11,7 +11,7 @@ use base_db::{ use hir_def::{ db::DefDatabase, hir::ExprId, layout::TargetDataLayout, AdtId, BlockId, CallableDefId, ConstParamId, DefWithBodyId, EnumVariantId, FunctionId, GeneralConstId, GenericDefId, ImplId, - LifetimeParamId, LocalFieldId, StaticId, TypeAliasId, TypeOrConstParamId, VariantId, + LifetimeParamId, LocalFieldId, StaticId, TraitId, TypeAliasId, TypeOrConstParamId, VariantId, }; use la_arena::ArenaMap; use smallvec::SmallVec; @@ -24,6 +24,7 @@ use crate::{ lower::{GenericDefaults, GenericPredicates}, method_resolution::{InherentImpls, TraitImpls, TyFingerprint}, mir::{BorrowckResult, MirBody, MirLowerError}, + object_safety::ObjectSafetyViolation, Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner, PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId, }; @@ -107,6 +108,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::layout::target_data_layout_query)] fn target_data_layout(&self, krate: CrateId) -> Result, Arc>; + #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)] + fn object_safety_of_trait(&self, trait_: TraitId) -> Option; + #[salsa::invoke(crate::lower::ty_query)] #[salsa::cycle(crate::lower::ty_recover)] fn ty(&self, def: TyDefId) -> Binders; @@ -150,6 +154,9 @@ pub trait HirDatabase: DefDatabase + Upcast { #[salsa::invoke(crate::lower::generic_predicates_query)] fn generic_predicates(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::generic_predicates_without_parent_query)] + fn generic_predicates_without_parent(&self, def: GenericDefId) -> GenericPredicates; + #[salsa::invoke(crate::lower::trait_environment_for_body_query)] #[salsa::transparent] fn trait_environment_for_body(&self, def: DefWithBodyId) -> Arc; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs index 024fc32f8637..82517e699175 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs @@ -16,13 +16,13 @@ mod case_conv; use std::fmt; use hir_def::{ - data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, AttrDefId, ConstId, - EnumId, EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, - StaticId, StructId, TraitId, TypeAliasId, + data::adt::VariantData, db::DefDatabase, hir::Pat, src::HasSource, AdtId, ConstId, EnumId, + EnumVariantId, FunctionId, HasModule, ItemContainerId, Lookup, ModuleDefId, ModuleId, StaticId, + StructId, TraitId, TypeAliasId, }; use hir_expand::{ name::{AsName, Name}, - HirFileId, HirFileIdExt, MacroFileIdExt, + HirFileId, HirFileIdExt, }; use intern::sym; use stdx::{always, never}; @@ -36,14 +36,6 @@ use crate::db::HirDatabase; use self::case_conv::{to_camel_case, to_lower_snake_case, to_upper_snake_case}; -mod allow { - pub(super) const BAD_STYLE: &str = "bad_style"; - pub(super) const NONSTANDARD_STYLE: &str = "nonstandard_style"; - pub(super) const NON_SNAKE_CASE: &str = "non_snake_case"; - pub(super) const NON_UPPER_CASE_GLOBAL: &str = "non_upper_case_globals"; - pub(super) const NON_CAMEL_CASE_TYPES: &str = "non_camel_case_types"; -} - pub fn incorrect_case(db: &dyn HirDatabase, owner: ModuleDefId) -> Vec { let _p = tracing::info_span!("incorrect_case").entered(); let mut validator = DeclValidator::new(db); @@ -160,92 +152,7 @@ impl<'a> DeclValidator<'a> { } } - /// Checks whether not following the convention is allowed for this item. - fn allowed(&self, id: AttrDefId, allow_name: &str, recursing: bool) -> bool { - let is_allowed = |def_id| { - let attrs = self.db.attrs(def_id); - // don't bug the user about directly no_mangle annotated stuff, they can't do anything about it - (!recursing && attrs.by_key(&sym::no_mangle).exists()) - || attrs.by_key(&sym::allow).tt_values().any(|tt| { - let allows = tt.to_string(); - allows.contains(allow_name) - || allows.contains(allow::BAD_STYLE) - || allows.contains(allow::NONSTANDARD_STYLE) - }) - }; - let db = self.db.upcast(); - let file_id_is_derive = || { - match id { - AttrDefId::ModuleId(m) => { - m.def_map(db)[m.local_id].origin.file_id().map(Into::into) - } - AttrDefId::FunctionId(f) => Some(f.lookup(db).id.file_id()), - AttrDefId::StaticId(sid) => Some(sid.lookup(db).id.file_id()), - AttrDefId::ConstId(cid) => Some(cid.lookup(db).id.file_id()), - AttrDefId::TraitId(tid) => Some(tid.lookup(db).id.file_id()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).id.file_id()), - AttrDefId::ImplId(iid) => Some(iid.lookup(db).id.file_id()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(db).id.file_id()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(db).id.file_id()), - AttrDefId::UseId(id) => Some(id.lookup(db).id.file_id()), - // These warnings should not explore macro definitions at all - AttrDefId::MacroId(_) => None, - AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(db).id.file_id()), - AdtId::EnumId(eid) => Some(eid.lookup(db).id.file_id()), - // Unions aren't yet supported - AdtId::UnionId(_) => None, - }, - AttrDefId::FieldId(_) => None, - AttrDefId::EnumVariantId(_) => None, - AttrDefId::TypeAliasId(_) => None, - AttrDefId::GenericParamId(_) => None, - } - .map_or(false, |file_id| { - matches!(file_id.macro_file(), Some(file_id) if file_id.is_custom_derive(db.upcast()) || file_id.is_builtin_derive(db.upcast())) - }) - }; - - let parent = || { - match id { - AttrDefId::ModuleId(m) => m.containing_module(db).map(|v| v.into()), - AttrDefId::FunctionId(f) => Some(f.lookup(db).container.into()), - AttrDefId::StaticId(sid) => Some(sid.lookup(db).container.into()), - AttrDefId::ConstId(cid) => Some(cid.lookup(db).container.into()), - AttrDefId::TraitId(tid) => Some(tid.lookup(db).container.into()), - AttrDefId::TraitAliasId(taid) => Some(taid.lookup(db).container.into()), - AttrDefId::ImplId(iid) => Some(iid.lookup(db).container.into()), - AttrDefId::ExternBlockId(id) => Some(id.lookup(db).container.into()), - AttrDefId::ExternCrateId(id) => Some(id.lookup(db).container.into()), - AttrDefId::UseId(id) => Some(id.lookup(db).container.into()), - // These warnings should not explore macro definitions at all - AttrDefId::MacroId(_) => None, - AttrDefId::AdtId(aid) => match aid { - AdtId::StructId(sid) => Some(sid.lookup(db).container.into()), - AdtId::EnumId(eid) => Some(eid.lookup(db).container.into()), - // Unions aren't yet supported - AdtId::UnionId(_) => None, - }, - AttrDefId::FieldId(_) => None, - AttrDefId::EnumVariantId(_) => None, - AttrDefId::TypeAliasId(_) => None, - AttrDefId::GenericParamId(_) => None, - } - .is_some_and(|mid| self.allowed(mid, allow_name, true)) - }; - is_allowed(id) - // FIXME: this is a hack to avoid false positives in derive macros currently - || file_id_is_derive() - // go upwards one step or give up - || parent() - } - fn validate_module(&mut self, module_id: ModuleId) { - // Check whether non-snake case identifiers are allowed for this module. - if self.allowed(module_id.into(), allow::NON_SNAKE_CASE, false) { - return; - } - // Check the module name. let Some(module_name) = module_id.name(self.db.upcast()) else { return }; let Some(module_name_replacement) = @@ -270,11 +177,6 @@ impl<'a> DeclValidator<'a> { } fn validate_trait(&mut self, trait_id: TraitId) { - // Check whether non-snake case identifiers are allowed for this trait. - if self.allowed(trait_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the trait name. let data = self.db.trait_data(trait_id); self.create_incorrect_case_diagnostic_for_item_name( @@ -292,21 +194,24 @@ impl<'a> DeclValidator<'a> { return; } - // Check whether non-snake case identifiers are allowed for this function. - if self.allowed(func.into(), allow::NON_SNAKE_CASE, false) { - return; - } - // Check the function name. // Skipped if function is an associated item of a trait implementation. if !self.is_trait_impl_container(container) { let data = self.db.function_data(func); - self.create_incorrect_case_diagnostic_for_item_name( - func, - &data.name, - CaseType::LowerSnakeCase, - IdentType::Function, - ); + + // Don't run the lint on extern "[not Rust]" fn items with the + // #[no_mangle] attribute. + let no_mangle = data.attrs.by_key(&sym::no_mangle).exists(); + if no_mangle && data.abi.as_ref().is_some_and(|abi| *abi != sym::Rust) { + cov_mark::hit!(extern_func_no_mangle_ignored); + } else { + self.create_incorrect_case_diagnostic_for_item_name( + func, + &data.name, + CaseType::LowerSnakeCase, + IdentType::Function, + ); + } } else { cov_mark::hit!(trait_impl_assoc_func_name_incorrect_case_ignored); } @@ -389,17 +294,13 @@ impl<'a> DeclValidator<'a> { fn validate_struct(&mut self, struct_id: StructId) { // Check the structure name. - let non_camel_case_allowed = - self.allowed(struct_id.into(), allow::NON_CAMEL_CASE_TYPES, false); - if !non_camel_case_allowed { - let data = self.db.struct_data(struct_id); - self.create_incorrect_case_diagnostic_for_item_name( - struct_id, - &data.name, - CaseType::UpperCamelCase, - IdentType::Structure, - ); - } + let data = self.db.struct_data(struct_id); + self.create_incorrect_case_diagnostic_for_item_name( + struct_id, + &data.name, + CaseType::UpperCamelCase, + IdentType::Structure, + ); // Check the field names. self.validate_struct_fields(struct_id); @@ -407,10 +308,6 @@ impl<'a> DeclValidator<'a> { /// Check incorrect names for struct fields. fn validate_struct_fields(&mut self, struct_id: StructId) { - if self.allowed(struct_id.into(), allow::NON_SNAKE_CASE, false) { - return; - } - let data = self.db.struct_data(struct_id); let VariantData::Record(fields) = data.variant_data.as_ref() else { return; @@ -484,11 +381,6 @@ impl<'a> DeclValidator<'a> { fn validate_enum(&mut self, enum_id: EnumId) { let data = self.db.enum_data(enum_id); - // Check whether non-camel case names are allowed for this enum. - if self.allowed(enum_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the enum name. self.create_incorrect_case_diagnostic_for_item_name( enum_id, @@ -653,10 +545,6 @@ impl<'a> DeclValidator<'a> { return; } - if self.allowed(const_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { - return; - } - let data = self.db.const_data(const_id); let Some(name) = &data.name else { return; @@ -676,10 +564,6 @@ impl<'a> DeclValidator<'a> { return; } - if self.allowed(static_id.into(), allow::NON_UPPER_CASE_GLOBAL, false) { - return; - } - self.create_incorrect_case_diagnostic_for_item_name( static_id, &data.name, @@ -695,11 +579,6 @@ impl<'a> DeclValidator<'a> { return; } - // Check whether non-snake case identifiers are allowed for this type alias. - if self.allowed(type_alias_id.into(), allow::NON_CAMEL_CASE_TYPES, false) { - return; - } - // Check the type alias name. let data = self.db.type_alias_data(type_alias_id); self.create_incorrect_case_diagnostic_for_item_name( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3f54cdd20cee..ff45c725c73c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs @@ -5,6 +5,7 @@ use hir_def::{ body::Body, hir::{Expr, ExprId, UnaryOp}, resolver::{resolver_for_expr, ResolveValueResult, Resolver, ValueNs}, + type_ref::Rawness, DefWithBodyId, }; @@ -12,7 +13,10 @@ use crate::{ db::HirDatabase, utils::is_fn_unsafe_to_call, InferenceResult, Interner, TyExt, TyKind, }; -pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { +/// Returns `(unsafe_exprs, fn_is_unsafe)`. +/// +/// If `fn_is_unsafe` is false, `unsafe_exprs` are hard errors. If true, they're `unsafe_op_in_unsafe_fn`. +pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> (Vec, bool) { let _p = tracing::info_span!("missing_unsafe").entered(); let mut res = Vec::new(); @@ -23,9 +27,6 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { | DefWithBodyId::VariantId(_) | DefWithBodyId::InTypeConstId(_) => false, }; - if is_unsafe { - return res; - } let body = db.body(def); let infer = db.infer(def); @@ -35,7 +36,7 @@ pub fn missing_unsafe(db: &dyn HirDatabase, def: DefWithBodyId) -> Vec { } }); - res + (res, is_unsafe) } pub struct UnsafeExpr { @@ -87,12 +88,20 @@ fn walk_unsafe( let g = resolver.update_to_inner_scope(db.upcast(), def, current); let value_or_partial = resolver.resolve_path_in_value_ns(db.upcast(), path); if let Some(ResolveValueResult::ValueNs(ValueNs::StaticId(id), _)) = value_or_partial { - if db.static_data(id).mutable { + let static_data = db.static_data(id); + if static_data.mutable || static_data.is_extern { unsafe_expr_cb(UnsafeExpr { expr: current, inside_unsafe_block }); } } resolver.reset_to_guard(g); } + Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => { + if let Expr::Path(_) = body.exprs[*expr] { + // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`, + // see https://github.com/rust-lang/rust/pull/125834. + return; + } + } Expr::MethodCall { .. } => { if infer .method_resolution(current) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs index a96c101a388b..89ca707c2e69 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/generics.rs @@ -225,6 +225,23 @@ impl Generics { } } +pub(crate) fn trait_self_param_idx(db: &dyn DefDatabase, def: GenericDefId) -> Option { + match def { + GenericDefId::TraitId(_) | GenericDefId::TraitAliasId(_) => { + let params = db.generic_params(def); + params.trait_self_param().map(|idx| idx.into_raw().into_u32() as usize) + } + GenericDefId::ImplId(_) => None, + _ => { + let parent_def = parent_generic_def(db, def)?; + let parent_params = db.generic_params(parent_def); + let parent_self_idx = parent_params.trait_self_param()?.into_raw().into_u32() as usize; + let self_params = db.generic_params(def); + Some(self_params.len() + parent_self_idx) + } + } +} + fn parent_generic_def(db: &dyn DefDatabase, def: GenericDefId) -> Option { let container = match def { GenericDefId::FunctionId(it) => it.lookup(db).container, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs index 062ea278151b..8bc3c50725d9 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs @@ -13,7 +13,7 @@ //! to certain types. To record this, we use the union-find implementation from //! the `ena` crate, which is extracted from rustc. -mod cast; +pub(crate) mod cast; pub(crate) mod closure; mod coerce; mod expr; @@ -76,7 +76,7 @@ pub use coerce::could_coerce; #[allow(unreachable_pub)] pub use unify::{could_unify, could_unify_deeply}; -use cast::CastCheck; +use cast::{CastCheck, CastError}; pub(crate) use closure::{CaptureKind, CapturedItem, CapturedItemWithoutTy}; /// The entry point of type inference. @@ -254,6 +254,16 @@ pub enum InferenceDiagnostic { expr: ExprId, expected: Ty, }, + CastToUnsized { + expr: ExprId, + cast_ty: Ty, + }, + InvalidCast { + expr: ExprId, + error: CastError, + expr_ty: Ty, + cast_ty: Ty, + }, } /// A mismatch between an expected and an inferred type. @@ -456,6 +466,7 @@ pub struct InferenceResult { pub(crate) closure_info: FxHashMap, FnTrait)>, // FIXME: remove this field pub mutated_bindings_in_closure: FxHashSet, + pub coercion_casts: FxHashSet, } impl InferenceResult { @@ -666,7 +677,7 @@ impl<'a> InferenceContext<'a> { let InferenceContext { mut table, mut result, - deferred_cast_checks, + mut deferred_cast_checks, tuple_field_accesses_rev, .. } = self; @@ -695,15 +706,25 @@ impl<'a> InferenceContext<'a> { closure_info: _, mutated_bindings_in_closure: _, tuple_field_access_types: _, + coercion_casts, } = &mut result; - table.fallback_if_possible(); // Comment from rustc: // Even though coercion casts provide type hints, we check casts after fallback for // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - for cast in deferred_cast_checks { - cast.check(&mut table); + let mut apply_adjustments = |expr, adj| { + expr_adjustments.insert(expr, adj); + }; + let mut set_coercion_cast = |expr| { + coercion_casts.insert(expr); + }; + for cast in deferred_cast_checks.iter_mut() { + if let Err(diag) = + cast.check(&mut table, &mut apply_adjustments, &mut set_coercion_cast) + { + diagnostics.push(diag); + } } // FIXME resolve obligations as well (use Guidance if necessary) @@ -732,7 +753,7 @@ impl<'a> InferenceContext<'a> { *has_errors = *has_errors || ty.contains_unknown(); } - *has_errors = !type_mismatches.is_empty(); + *has_errors |= !type_mismatches.is_empty(); type_mismatches.retain(|_, mismatch| { mismatch.expected = table.resolve_completely(mismatch.expected.clone()); @@ -775,20 +796,30 @@ impl<'a> InferenceContext<'a> { }); for (_, subst) in method_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for (_, subst) in assoc_resolutions.values_mut() { *subst = table.resolve_completely(subst.clone()); + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); } for adjustment in expr_adjustments.values_mut().flatten() { adjustment.target = table.resolve_completely(adjustment.target.clone()); + *has_errors = *has_errors || adjustment.target.contains_unknown(); } for adjustment in pat_adjustments.values_mut().flatten() { *adjustment = table.resolve_completely(adjustment.clone()); + *has_errors = *has_errors || adjustment.contains_unknown(); } result.tuple_field_access_types = tuple_field_accesses_rev .into_iter() .enumerate() .map(|(idx, subst)| (TupleId(idx as u32), table.resolve_completely(subst))) + .inspect(|(_, subst)| { + *has_errors = + *has_errors || subst.type_parameters(Interner).any(|ty| ty.contains_unknown()); + }) .collect(); result } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs index 060b5f36f292..caa3960a227c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs @@ -1,47 +1,451 @@ //! Type cast logic. Basically coercion + additional casts. -use crate::{infer::unify::InferenceTable, Interner, Ty, TyExt, TyKind}; +use chalk_ir::{Mutability, Scalar, TyVariableKind, UintTy}; +use hir_def::{hir::ExprId, AdtId}; +use stdx::never; + +use crate::{ + infer::unify::InferenceTable, Adjustment, Binders, DynTy, InferenceDiagnostic, Interner, + PlaceholderIndex, QuantifiedWhereClauses, Ty, TyExt, TyKind, TypeFlags, WhereClause, +}; + +#[derive(Debug)] +pub(crate) enum Int { + I, + U(UintTy), + Bool, + Char, + CEnum, + InferenceVar, +} + +#[derive(Debug)] +pub(crate) enum CastTy { + Int(Int), + Float, + FnPtr, + Ptr(Ty, Mutability), + // `DynStar` is Not supported yet in r-a +} + +impl CastTy { + pub(crate) fn from_ty(table: &mut InferenceTable<'_>, t: &Ty) -> Option { + match t.kind(Interner) { + TyKind::Scalar(Scalar::Bool) => Some(Self::Int(Int::Bool)), + TyKind::Scalar(Scalar::Char) => Some(Self::Int(Int::Char)), + TyKind::Scalar(Scalar::Int(_)) => Some(Self::Int(Int::I)), + TyKind::Scalar(Scalar::Uint(it)) => Some(Self::Int(Int::U(*it))), + TyKind::InferenceVar(_, TyVariableKind::Integer) => Some(Self::Int(Int::InferenceVar)), + TyKind::InferenceVar(_, TyVariableKind::Float) => Some(Self::Float), + TyKind::Scalar(Scalar::Float(_)) => Some(Self::Float), + TyKind::Adt(..) => { + let (AdtId::EnumId(id), _) = t.as_adt()? else { + return None; + }; + let enum_data = table.db.enum_data(id); + if enum_data.is_payload_free(table.db.upcast()) { + Some(Self::Int(Int::CEnum)) + } else { + None + } + } + TyKind::Raw(m, ty) => Some(Self::Ptr(table.resolve_ty_shallow(ty), *m)), + TyKind::Function(_) => Some(Self::FnPtr), + _ => None, + } + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum CastError { + Unknown, + CastToBool, + CastToChar, + DifferingKinds, + SizedUnsizedCast, + IllegalCast, + IntToFatCast, + NeedDeref, + NeedViaPtr, + NeedViaThinPtr, + NeedViaInt, + NonScalar, + UnknownCastPtrKind, + UnknownExprPtrKind, +} + +impl CastError { + fn into_diagnostic(self, expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> InferenceDiagnostic { + InferenceDiagnostic::InvalidCast { expr, error: self, expr_ty, cast_ty } + } +} #[derive(Clone, Debug)] pub(super) struct CastCheck { + expr: ExprId, + source_expr: ExprId, expr_ty: Ty, cast_ty: Ty, } impl CastCheck { - pub(super) fn new(expr_ty: Ty, cast_ty: Ty) -> Self { - Self { expr_ty, cast_ty } + pub(super) fn new(expr: ExprId, source_expr: ExprId, expr_ty: Ty, cast_ty: Ty) -> Self { + Self { expr, source_expr, expr_ty, cast_ty } } - pub(super) fn check(self, table: &mut InferenceTable<'_>) { - // FIXME: This function currently only implements the bits that influence the type - // inference. We should return the adjustments on success and report diagnostics on error. - let expr_ty = table.resolve_ty_shallow(&self.expr_ty); - let cast_ty = table.resolve_ty_shallow(&self.cast_ty); + pub(super) fn check( + &mut self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + set_coercion_cast: &mut G, + ) -> Result<(), InferenceDiagnostic> + where + F: FnMut(ExprId, Vec), + G: FnMut(ExprId), + { + table.resolve_obligations_as_possible(); + self.expr_ty = table.resolve_ty_shallow(&self.expr_ty); + self.cast_ty = table.resolve_ty_shallow(&self.cast_ty); - if table.coerce(&expr_ty, &cast_ty).is_ok() { - return; + if self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + return Ok(()); } - if check_ref_to_ptr_cast(expr_ty, cast_ty, table) { - // Note that this type of cast is actually split into a coercion to a - // pointer type and a cast: - // &[T; N] -> *[T; N] -> *T + if !self.cast_ty.data(Interner).flags.contains(TypeFlags::HAS_TY_INFER) + && !table.is_sized(&self.cast_ty) + { + return Err(InferenceDiagnostic::CastToUnsized { + expr: self.expr, + cast_ty: self.cast_ty.clone(), + }); } - // FIXME: Check other kinds of non-coercion casts and report error if any? + // Chalk doesn't support trait upcasting and fails to solve some obvious goals + // when the trait environment contains some recursive traits (See issue #18047) + // We skip cast checks for such cases for now, until the next-gen solver. + if contains_dyn_trait(&self.cast_ty) { + return Ok(()); + } + + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &self.cast_ty) { + apply_adjustments(self.source_expr, adj); + set_coercion_cast(self.source_expr); + return Ok(()); + } + + self.do_check(table, apply_adjustments) + .map_err(|e| e.into_diagnostic(self.expr, self.expr_ty.clone(), self.cast_ty.clone())) + } + + fn do_check( + &self, + table: &mut InferenceTable<'_>, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + let (t_from, t_cast) = + match (CastTy::from_ty(table, &self.expr_ty), CastTy::from_ty(table, &self.cast_ty)) { + (Some(t_from), Some(t_cast)) => (t_from, t_cast), + (None, Some(t_cast)) => match self.expr_ty.kind(Interner) { + TyKind::FnDef(..) => { + let sig = self.expr_ty.callable_sig(table.db).expect("FnDef had no sig"); + let sig = table.normalize_associated_types_in(sig); + let fn_ptr = TyKind::Function(sig.to_fn_ptr()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &fn_ptr) { + apply_adjustments(self.source_expr, adj); + } else { + return Err(CastError::IllegalCast); + } + + (CastTy::FnPtr, t_cast) + } + TyKind::Ref(mutbl, _, inner_ty) => { + let inner_ty = table.resolve_ty_shallow(inner_ty); + return match t_cast { + CastTy::Int(_) | CastTy::Float => match inner_ty.kind(Interner) { + TyKind::Scalar( + Scalar::Int(_) | Scalar::Uint(_) | Scalar::Float(_), + ) + | TyKind::InferenceVar( + _, + TyVariableKind::Integer | TyVariableKind::Float, + ) => Err(CastError::NeedDeref), + + _ => Err(CastError::NeedViaPtr), + }, + // array-ptr-cast + CastTy::Ptr(t, m) => { + let t = table.resolve_ty_shallow(&t); + if !table.is_sized(&t) { + return Err(CastError::IllegalCast); + } + self.check_ref_cast( + table, + &inner_ty, + *mutbl, + &t, + m, + apply_adjustments, + ) + } + _ => Err(CastError::NonScalar), + }; + } + _ => return Err(CastError::NonScalar), + }, + _ => return Err(CastError::NonScalar), + }; + + // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym + + match (t_from, t_cast) { + (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar), + (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool), + (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), + (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar), + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => { + Err(CastError::NeedViaInt) + } + (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..)) + | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast), + (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => { + self.check_ptr_ptr_cast(table, &src, &dst) + } + (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(table, &src), + (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(table, &dst), + (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(table, &dst), + (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()), + (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()), + (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()), + (CastTy::FnPtr, CastTy::Int(_)) => Ok(()), + } + } + + fn check_ref_cast( + &self, + table: &mut InferenceTable<'_>, + t_expr: &Ty, + m_expr: Mutability, + t_cast: &Ty, + m_cast: Mutability, + apply_adjustments: &mut F, + ) -> Result<(), CastError> + where + F: FnMut(ExprId, Vec), + { + // Mutability order is opposite to rustc. `Mut < Not` + if m_expr <= m_cast { + if let TyKind::Array(ety, _) = t_expr.kind(Interner) { + // Coerce to a raw pointer so that we generate RawPtr in MIR. + let array_ptr_type = TyKind::Raw(m_expr, t_expr.clone()).intern(Interner); + if let Ok((adj, _)) = table.coerce(&self.expr_ty, &array_ptr_type) { + apply_adjustments(self.source_expr, adj); + } else { + never!( + "could not cast from reference to array to pointer to array ({:?} to {:?})", + self.expr_ty, + array_ptr_type + ); + } + + // This is a less strict condition than rustc's `demand_eqtype`, + // but false negative is better than false positive + if table.coerce(ety, t_cast).is_ok() { + return Ok(()); + } + } + } + + Err(CastError::IllegalCast) + } + + fn check_ptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + src: &Ty, + dst: &Ty, + ) -> Result<(), CastError> { + let src_kind = pointer_kind(src, table).map_err(|_| CastError::Unknown)?; + let dst_kind = pointer_kind(dst, table).map_err(|_| CastError::Unknown)?; + + match (src_kind, dst_kind) { + (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()), + (_, None) => Err(CastError::UnknownCastPtrKind), + (_, Some(PointerKind::Thin)) => Ok(()), + (None, _) => Err(CastError::UnknownExprPtrKind), + (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast), + (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => { + let principal = |tty: &Binders| { + tty.skip_binders().as_slice(Interner).first().and_then(|pred| { + if let WhereClause::Implemented(tr) = pred.skip_binders() { + Some(tr.trait_id) + } else { + None + } + }) + }; + match (principal(&src_tty), principal(&dst_tty)) { + (Some(src_principal), Some(dst_principal)) => { + if src_principal == dst_principal { + return Ok(()); + } + let src_principal = + table.db.trait_datum(table.trait_env.krate, src_principal); + let dst_principal = + table.db.trait_datum(table.trait_env.krate, dst_principal); + if src_principal.is_auto_trait() && dst_principal.is_auto_trait() { + Ok(()) + } else { + Err(CastError::DifferingKinds) + } + } + _ => Err(CastError::Unknown), + } + } + (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()), + (_, _) => Err(CastError::DifferingKinds), + } + } + + fn check_ptr_addr_cast( + &self, + table: &mut InferenceTable<'_>, + expr_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(expr_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownExprPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::NeedViaThinPtr), + } + } + + fn check_addr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast), + Some(PointerKind::Length) => Err(CastError::IntToFatCast), + Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast), + } + } + + fn check_fptr_ptr_cast( + &self, + table: &mut InferenceTable<'_>, + cast_ty: &Ty, + ) -> Result<(), CastError> { + match pointer_kind(cast_ty, table).map_err(|_| CastError::Unknown)? { + None => Err(CastError::UnknownCastPtrKind), + Some(PointerKind::Error) => Ok(()), + Some(PointerKind::Thin) => Ok(()), + _ => Err(CastError::IllegalCast), + } } } -fn check_ref_to_ptr_cast(expr_ty: Ty, cast_ty: Ty, table: &mut InferenceTable<'_>) -> bool { - let Some((expr_inner_ty, _, _)) = expr_ty.as_reference() else { - return false; - }; - let Some((cast_inner_ty, _)) = cast_ty.as_raw_ptr() else { - return false; - }; - let TyKind::Array(expr_elt_ty, _) = expr_inner_ty.kind(Interner) else { - return false; - }; - table.coerce(expr_elt_ty, cast_inner_ty).is_ok() +#[derive(PartialEq, Eq)] +enum PointerKind { + // thin pointer + Thin, + // trait object + VTable(Binders), + // slice + Length, + OfAlias, + OfParam(PlaceholderIndex), + Error, +} + +fn pointer_kind(ty: &Ty, table: &mut InferenceTable<'_>) -> Result, ()> { + let ty = table.resolve_ty_shallow(ty); + + if table.is_sized(&ty) { + return Ok(Some(PointerKind::Thin)); + } + + match ty.kind(Interner) { + TyKind::Slice(_) | TyKind::Str => Ok(Some(PointerKind::Length)), + TyKind::Dyn(DynTy { bounds, .. }) => Ok(Some(PointerKind::VTable(bounds.clone()))), + TyKind::Adt(chalk_ir::AdtId(id), subst) => { + let AdtId::StructId(id) = *id else { + never!("`{:?}` should be sized but is not?", ty); + return Err(()); + }; + + let struct_data = table.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + table.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + pointer_kind(&last_field_ty, table) + } else { + Ok(Some(PointerKind::Thin)) + } + } + TyKind::Tuple(_, subst) => { + match subst.iter(Interner).last().and_then(|arg| arg.ty(Interner)) { + None => Ok(Some(PointerKind::Thin)), + Some(ty) => pointer_kind(ty, table), + } + } + TyKind::Foreign(_) => Ok(Some(PointerKind::Thin)), + TyKind::Alias(_) | TyKind::AssociatedType(..) | TyKind::OpaqueType(..) => { + Ok(Some(PointerKind::OfAlias)) + } + TyKind::Error => Ok(Some(PointerKind::Error)), + TyKind::Placeholder(idx) => Ok(Some(PointerKind::OfParam(*idx))), + TyKind::BoundVar(_) | TyKind::InferenceVar(..) => Ok(None), + TyKind::Scalar(_) + | TyKind::Array(..) + | TyKind::CoroutineWitness(..) + | TyKind::Raw(..) + | TyKind::Ref(..) + | TyKind::FnDef(..) + | TyKind::Function(_) + | TyKind::Closure(..) + | TyKind::Coroutine(..) + | TyKind::Never => { + never!("`{:?}` should be sized but is not?", ty); + Err(()) + } + } +} + +fn contains_dyn_trait(ty: &Ty) -> bool { + use std::ops::ControlFlow; + + use chalk_ir::{ + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, + }; + + struct DynTraitVisitor; + + impl TypeVisitor for DynTraitVisitor { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::Dyn(_) => ControlFlow::Break(()), + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + } + + ty.visit_with(DynTraitVisitor.as_dyn(), DebruijnIndex::INNERMOST).is_break() } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index 36327d1d49c0..5cad08b93956 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -10,7 +10,10 @@ use chalk_ir::{ use either::Either; use hir_def::{ data::adt::VariantData, - hir::{Array, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, Pat, PatId, Statement, + UnaryOp, + }, lang_item::LangItem, resolver::{resolver_for_expr, ResolveValueResult, ValueNs}, DefWithBodyId, FieldId, HasModule, TupleFieldId, TupleId, VariantId, @@ -666,7 +669,21 @@ impl InferenceContext<'_> { fn walk_expr_without_adjust(&mut self, tgt_expr: ExprId) { match &self.body[tgt_expr] { Expr::OffsetOf(_) => (), - Expr::InlineAsm(e) => self.walk_expr_without_adjust(e.e), + Expr::InlineAsm(e) => e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => self.walk_expr_without_adjust(*expr), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.walk_expr_without_adjust(*in_expr); + if let Some(out_expr) = out_expr { + self.walk_expr_without_adjust(*out_expr); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Const(_) + | AsmOperand::Label(_) + | AsmOperand::Sym(_) => (), + }), Expr::If { condition, then_branch, else_branch } => { self.consume_expr(*condition); self.consume_expr(*then_branch); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs index 89d92ea9af0b..a04e7b17ae6e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs @@ -9,7 +9,8 @@ use chalk_ir::{cast::Cast, fold::Shift, DebruijnIndex, Mutability, TyVariableKin use either::Either; use hir_def::{ hir::{ - ArithOp, Array, BinaryOp, ClosureKind, Expr, ExprId, LabelId, Literal, Statement, UnaryOp, + ArithOp, Array, AsmOperand, AsmOptions, BinaryOp, ClosureKind, Expr, ExprId, LabelId, + Literal, Statement, UnaryOp, }, lang_item::{LangItem, LangItemTarget}, path::{GenericArg, GenericArgs, Path}, @@ -41,9 +42,9 @@ use crate::{ primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, traits::FnTrait, - Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, FnAbi, FnPointer, FnSig, - FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, - TyExt, TyKind, + Adjust, Adjustment, AdtId, AutoBorrow, Binders, CallableDefId, CallableSig, FnAbi, FnPointer, + FnSig, FnSubst, Interner, Rawness, Scalar, Substitution, TraitEnvironment, TraitRef, Ty, + TyBuilder, TyExt, TyKind, }; use super::{ @@ -610,7 +611,12 @@ impl InferenceContext<'_> { Expr::Cast { expr, type_ref } => { let cast_ty = self.make_ty(type_ref); let expr_ty = self.infer_expr(*expr, &Expectation::Castable(cast_ty.clone())); - self.deferred_cast_checks.push(CastCheck::new(expr_ty, cast_ty.clone())); + self.deferred_cast_checks.push(CastCheck::new( + tgt_expr, + *expr, + expr_ty, + cast_ty.clone(), + )); cast_ty } Expr::Ref { expr, rawness, mutability } => { @@ -845,7 +851,7 @@ impl InferenceContext<'_> { }; for (expr, ty) in exprs.iter().zip(tys.iter_mut()) { - self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); + *ty = self.infer_expr_coerce(*expr, &Expectation::has_type(ty.clone())); } TyKind::Tuple(tys.len(), Substitution::from_iter(Interner, tys)).intern(Interner) @@ -889,21 +895,52 @@ impl InferenceContext<'_> { TyKind::Scalar(Scalar::Int(primitive::int_ty_from_builtin(*int_ty))) .intern(Interner) } - None => self.table.new_integer_var(), + None => { + let expected_ty = expected.to_option(&mut self.table); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { + Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, + Some(TyKind::Scalar(Scalar::Char)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) + } + Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + } }, Literal::Uint(_v, ty) => match ty { Some(int_ty) => { TyKind::Scalar(Scalar::Uint(primitive::uint_ty_from_builtin(*int_ty))) .intern(Interner) } - None => self.table.new_integer_var(), + None => { + let expected_ty = expected.to_option(&mut self.table); + let opt_ty = match expected_ty.as_ref().map(|it| it.kind(Interner)) { + Some(TyKind::Scalar(Scalar::Int(_) | Scalar::Uint(_))) => expected_ty, + Some(TyKind::Scalar(Scalar::Char)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner)) + } + Some(TyKind::Raw(..) | TyKind::FnDef(..) | TyKind::Function(..)) => { + Some(TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner)) + } + _ => None, + }; + opt_ty.unwrap_or_else(|| self.table.new_integer_var()) + } }, Literal::Float(_v, ty) => match ty { Some(float_ty) => { TyKind::Scalar(Scalar::Float(primitive::float_ty_from_builtin(*float_ty))) .intern(Interner) } - None => self.table.new_float_var(), + None => { + let opt_ty = expected.to_option(&mut self.table).filter(|ty| { + matches!(ty.kind(Interner), TyKind::Scalar(Scalar::Float(_))) + }); + opt_ty.unwrap_or_else(|| self.table.new_float_var()) + } }, }, Expr::Underscore => { @@ -919,9 +956,61 @@ impl InferenceContext<'_> { expected } Expr::OffsetOf(_) => TyKind::Scalar(Scalar::Uint(UintTy::Usize)).intern(Interner), - Expr::InlineAsm(it) => { - self.infer_expr_no_expect(it.e); - self.result.standard_types.unit.clone() + Expr::InlineAsm(asm) => { + let mut check_expr_asm_operand = |expr, is_input: bool| { + let ty = self.infer_expr_no_expect(expr); + + // If this is an input value, we require its type to be fully resolved + // at this point. This allows us to provide helpful coercions which help + // pass the type candidate list in a later pass. + // + // We don't require output types to be resolved at this point, which + // allows them to be inferred based on how they are used later in the + // function. + if is_input { + let ty = self.resolve_ty_shallow(&ty); + match ty.kind(Interner) { + TyKind::FnDef(def, parameters) => { + let fnptr_ty = TyKind::Function( + CallableSig::from_def(self.db, *def, parameters).to_fn_ptr(), + ) + .intern(Interner); + _ = self.coerce(Some(expr), &ty, &fnptr_ty); + } + TyKind::Ref(mutbl, _, base_ty) => { + let ptr_ty = TyKind::Raw(*mutbl, base_ty.clone()).intern(Interner); + _ = self.coerce(Some(expr), &ty, &ptr_ty); + } + _ => {} + } + } + }; + + let diverge = asm.options.contains(AsmOptions::NORETURN); + asm.operands.iter().for_each(|(_, operand)| match *operand { + AsmOperand::In { expr, .. } => check_expr_asm_operand(expr, true), + AsmOperand::Out { expr: Some(expr), .. } | AsmOperand::InOut { expr, .. } => { + check_expr_asm_operand(expr, false) + } + AsmOperand::Out { expr: None, .. } => (), + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + check_expr_asm_operand(in_expr, true); + if let Some(out_expr) = out_expr { + check_expr_asm_operand(out_expr, false); + } + } + // FIXME + AsmOperand::Label(_) => (), + // FIXME + AsmOperand::Const(_) => (), + // FIXME + AsmOperand::Sym(_) => (), + }); + if diverge { + self.result.standard_types.never.clone() + } else { + self.result.standard_types.unit.clone() + } } }; // use a new type variable if we got unknown here diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs index 7fed5f0203ba..8e52725e5360 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs @@ -3,7 +3,9 @@ use chalk_ir::{cast::Cast, Mutability}; use hir_def::{ - hir::{Array, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp}, + hir::{ + Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, PatId, Statement, UnaryOp, + }, lang_item::LangItem, }; use hir_expand::name::Name; @@ -39,7 +41,25 @@ impl InferenceContext<'_> { fn infer_mut_expr_without_adjust(&mut self, tgt_expr: ExprId, mutability: Mutability) { match &self.body[tgt_expr] { Expr::Missing => (), - Expr::InlineAsm(e) => self.infer_mut_expr_without_adjust(e.e, Mutability::Not), + Expr::InlineAsm(e) => { + e.operands.iter().for_each(|(_, op)| match op { + AsmOperand::In { expr, .. } + | AsmOperand::Out { expr: Some(expr), .. } + | AsmOperand::InOut { expr, .. } => { + self.infer_mut_expr_without_adjust(*expr, Mutability::Not) + } + AsmOperand::SplitInOut { in_expr, out_expr, .. } => { + self.infer_mut_expr_without_adjust(*in_expr, Mutability::Not); + if let Some(out_expr) = out_expr { + self.infer_mut_expr_without_adjust(*out_expr, Mutability::Not); + } + } + AsmOperand::Out { expr: None, .. } + | AsmOperand::Label(_) + | AsmOperand::Sym(_) + | AsmOperand::Const(_) => (), + }); + } Expr::OffsetOf(_) => (), &Expr::If { condition, then_branch, else_branch } => { self.infer_mut_expr(condition, Mutability::Not); @@ -129,7 +149,7 @@ impl InferenceContext<'_> { target, }) = base_adjustments { - // For assignee exprs `IndexMut` obiligations are already applied + // For assignee exprs `IndexMut` obligations are already applied if !is_assignee_expr { if let TyKind::Ref(_, _, ty) = target.kind(Interner) { base_ty = Some(ty.clone()); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs index 0b44bbec70f7..e4841c7b15b6 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/path.rs @@ -247,8 +247,12 @@ impl InferenceContext<'_> { &self.resolver, self.owner.into(), ); - let trait_ref = - ctx.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = ctx.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + self.table.new_type_var(), + ); + self.resolve_trait_assoc_item(trait_ref, segment, id) } (def, _) => { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs index c0f5ddddcbe3..7300453ff001 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs @@ -9,6 +9,7 @@ use chalk_ir::{ use chalk_solve::infer::ParameterEnaVariableExt; use either::Either; use ena::unify::UnifyKey; +use hir_def::{lang_item::LangItem, AdtId}; use hir_expand::name::Name; use intern::sym; use rustc_hash::FxHashMap; @@ -21,7 +22,7 @@ use crate::{ to_chalk_trait_id, traits::FnTrait, AliasEq, AliasTy, BoundVar, Canonical, Const, ConstValue, DebruijnIndex, DomainGoal, GenericArg, GenericArgData, Goal, GoalData, Guidance, InEnvironment, InferenceVar, Interner, Lifetime, OpaqueTyId, ParamKind, ProjectionTy, ProjectionTyExt, Scalar, - Solution, Substitution, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind, VariableKind, + Solution, Substitution, TraitEnvironment, TraitRef, Ty, TyBuilder, TyExt, TyKind, VariableKind, WhereClause, }; @@ -265,14 +266,16 @@ impl<'a> InferenceTable<'a> { } let v = InferenceVar::from(i as u32); let root = self.var_unification_table.inference_var_root(v); - if let Some(data) = self.type_variable_table.get_mut(root.index() as usize) { - *data |= TypeVariableFlags::DIVERGING; - } + self.modify_type_variable_flag(root, |f| { + *f |= TypeVariableFlags::DIVERGING; + }); } } pub(super) fn set_diverging(&mut self, iv: InferenceVar, diverging: bool) { - self.type_variable_table[iv.index() as usize].set(TypeVariableFlags::DIVERGING, diverging); + self.modify_type_variable_flag(iv, |f| { + f.set(TypeVariableFlags::DIVERGING, diverging); + }); } fn fallback_value(&self, iv: InferenceVar, kind: TyVariableKind) -> Ty { @@ -369,6 +372,18 @@ impl<'a> InferenceTable<'a> { var } + fn modify_type_variable_flag(&mut self, var: InferenceVar, cb: F) + where + F: FnOnce(&mut TypeVariableFlags), + { + let idx = var.index() as usize; + if self.type_variable_table.len() <= idx { + self.extend_type_variable_table(idx); + } + if let Some(f) = self.type_variable_table.get_mut(idx) { + cb(f); + } + } fn extend_type_variable_table(&mut self, to_index: usize) { let count = to_index - self.type_variable_table.len() + 1; self.type_variable_table.extend(iter::repeat(TypeVariableFlags::default()).take(count)); @@ -898,6 +913,37 @@ impl<'a> InferenceTable<'a> { _ => c, } } + + /// Check if given type is `Sized` or not + pub(crate) fn is_sized(&mut self, ty: &Ty) -> bool { + // Early return for some obvious types + if matches!(ty.kind(Interner), TyKind::Scalar(..) | TyKind::Ref(..) | TyKind::Raw(..)) { + return true; + } + if let Some((AdtId::StructId(id), subst)) = ty.as_adt() { + let struct_data = self.db.struct_data(id); + if let Some((last_field, _)) = struct_data.variant_data.fields().iter().last() { + let last_field_ty = + self.db.field_types(id.into())[last_field].clone().substitute(Interner, subst); + // Structs can have DST as its last field and such cases are not handled + // as unsized by the chalk, so we do this manually + return self.is_sized(&last_field_ty); + } + } + let Some(sized) = self + .db + .lang_item(self.trait_env.krate, LangItem::Sized) + .and_then(|sized| sized.as_trait()) + else { + return false; + }; + let sized_pred = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(sized), + substitution: Substitution::from1(Interner, ty.clone()), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(sized_pred)).intern(Interner); + matches!(self.try_obligation(goal), Some(Solution::Unique(_))) + } } impl fmt::Debug for InferenceTable<'_> { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs index 25362d23d554..4cdc0db46a15 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs @@ -72,6 +72,7 @@ pub type Variants = hir_def::layout::Variants) -> fmt::Result { match self { + LayoutError::EmptyUnion => write!(f, "type is an union with no fields"), LayoutError::HasErrorConst => write!(f, "type contains an unevaluatable const"), LayoutError::HasErrorType => write!(f, "type contains an error"), LayoutError::HasPlaceholder => write!(f, "type contains placeholders"), @@ -98,6 +101,9 @@ impl fmt::Display for LayoutError { } LayoutError::SizeOverflow => write!(f, "size overflow"), LayoutError::TargetLayoutNotAvailable => write!(f, "target layout not available"), + LayoutError::UnexpectedUnsized => { + write!(f, "an unsized type was found where a sized type was expected") + } LayoutError::Unknown => write!(f, "unknown"), LayoutError::UserReprTooSmall => { write!(f, "the `#[repr]` hint is too small to hold the discriminants of the enum") @@ -109,9 +115,8 @@ impl fmt::Display for LayoutError { impl From> for LayoutError { fn from(err: LayoutCalculatorError) -> Self { match err { - LayoutCalculatorError::UnexpectedUnsized(_) | LayoutCalculatorError::EmptyUnion => { - LayoutError::Unknown - } + LayoutCalculatorError::EmptyUnion => LayoutError::EmptyUnion, + LayoutCalculatorError::UnexpectedUnsized(_) => LayoutError::UnexpectedUnsized, LayoutCalculatorError::SizeOverflow => LayoutError::SizeOverflow, } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs index 9b1424548c2a..7d77f6d0731a 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs @@ -11,8 +11,8 @@ pub fn target_data_layout_query( db: &dyn HirDatabase, krate: CrateId, ) -> Result, Arc> { - match db.data_layout(krate) { - Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(&it) { + match &db.crate_workspace_data()[&krate].data_layout { + Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it) { Ok(it) => Ok(Arc::new(it)), Err(e) => { Err(match e { @@ -42,6 +42,6 @@ pub fn target_data_layout_query( }.into()) } }, - Err(e) => Err(e), + Err(e) => Err(e.clone()), } } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs index 26ab02558a1b..5ed41b99ba3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs @@ -42,6 +42,7 @@ pub mod lang_items; pub mod layout; pub mod method_resolution; pub mod mir; +pub mod object_safety; pub mod primitive; pub mod traits; @@ -82,6 +83,7 @@ pub use autoderef::autoderef; pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ + cast::CastError, closure::{CaptureKind, CapturedItem}, could_coerce, could_unify, could_unify_deeply, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, InferenceResult, OverloadedDeref, PointerCast, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 370d9ba99cb0..c6c2108e34af 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -58,7 +58,7 @@ use crate::{ }, db::HirDatabase, error_lifetime, - generics::{generics, Generics}, + generics::{generics, trait_self_param_idx, Generics}, make_binders, mapping::{from_chalk_trait_id, lt_to_placeholder_idx, ToChalk}, static_lifetime, to_assoc_type_id, to_chalk_trait_id, to_placeholder_idx, @@ -516,8 +516,11 @@ impl<'a> TyLoweringContext<'a> { TypeNs::TraitId(trait_) => { let ty = match remaining_segments.len() { 1 => { - let trait_ref = - self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, None); + let trait_ref = self.lower_trait_ref_from_resolved_path( + trait_, + resolved_segment, + TyKind::Error.intern(Interner), + ); let segment = remaining_segments.first().unwrap(); let found = self .db @@ -952,11 +955,17 @@ impl<'a> TyLoweringContext<'a> { Substitution::from_iter(Interner, substs) } - fn lower_trait_ref_from_path( + pub(crate) fn lower_trait_ref_from_resolved_path( &self, - path: &Path, - explicit_self_ty: Option, - ) -> Option { + resolved: TraitId, + segment: PathSegment<'_>, + explicit_self_ty: Ty, + ) -> TraitRef { + let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); + TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } + } + + fn lower_trait_ref_from_path(&self, path: &Path, explicit_self_ty: Ty) -> Option { let resolved = match self.resolver.resolve_path_in_type_ns_fully(self.db.upcast(), path)? { // FIXME(trait_alias): We need to handle trait alias here. TypeNs::TraitId(tr) => tr, @@ -966,21 +975,7 @@ impl<'a> TyLoweringContext<'a> { Some(self.lower_trait_ref_from_resolved_path(resolved, segment, explicit_self_ty)) } - pub(crate) fn lower_trait_ref_from_resolved_path( - &self, - resolved: TraitId, - segment: PathSegment<'_>, - explicit_self_ty: Option, - ) -> TraitRef { - let substs = self.trait_ref_substs_from_path(segment, resolved, explicit_self_ty); - TraitRef { trait_id: to_chalk_trait_id(resolved), substitution: substs } - } - - fn lower_trait_ref( - &self, - trait_ref: &HirTraitRef, - explicit_self_ty: Option, - ) -> Option { + fn lower_trait_ref(&self, trait_ref: &HirTraitRef, explicit_self_ty: Ty) -> Option { self.lower_trait_ref_from_path(&trait_ref.path, explicit_self_ty) } @@ -988,9 +983,9 @@ impl<'a> TyLoweringContext<'a> { &self, segment: PathSegment<'_>, resolved: TraitId, - explicit_self_ty: Option, + explicit_self_ty: Ty, ) -> Substitution { - self.substs_from_path_segment(segment, Some(resolved.into()), false, explicit_self_ty) + self.substs_from_path_segment(segment, Some(resolved.into()), false, Some(explicit_self_ty)) } pub(crate) fn lower_where_predicate<'b>( @@ -1041,7 +1036,7 @@ impl<'a> TyLoweringContext<'a> { let mut trait_ref = None; let clause = match bound.as_ref() { TypeBound::Path(path, TraitBoundModifier::None) => { - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Path(path, TraitBoundModifier::Maybe) => { @@ -1053,7 +1048,7 @@ impl<'a> TyLoweringContext<'a> { // `?Sized` has no of them. // If we got another trait here ignore the bound completely. let trait_id = self - .lower_trait_ref_from_path(path, Some(self_ty.clone())) + .lower_trait_ref_from_path(path, self_ty.clone()) .map(|trait_ref| trait_ref.hir_trait_id()); if trait_id == sized_trait { self.unsized_types.borrow_mut().insert(self_ty); @@ -1062,7 +1057,7 @@ impl<'a> TyLoweringContext<'a> { } TypeBound::ForLifetime(_, path) => { // FIXME Don't silently drop the hrtb lifetimes here - trait_ref = self.lower_trait_ref_from_path(path, Some(self_ty)); + trait_ref = self.lower_trait_ref_from_path(path, self_ty); trait_ref.clone().map(WhereClause::Implemented).map(crate::wrap_empty_binders) } TypeBound::Lifetime(l) => { @@ -1700,6 +1695,28 @@ pub(crate) fn generic_predicates_query( db: &dyn HirDatabase, def: GenericDefId, ) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, _| true) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +pub(crate) fn generic_predicates_without_parent_query( + db: &dyn HirDatabase, + def: GenericDefId, +) -> GenericPredicates { + generic_predicates_filtered_by(db, def, |_, d| *d == def) +} + +/// Resolve the where clause(s) of an item with generics, +/// except the ones inherited from the parent +fn generic_predicates_filtered_by( + db: &dyn HirDatabase, + def: GenericDefId, + filter: F, +) -> GenericPredicates +where + F: Fn(&WherePredicate, &GenericDefId) -> bool, +{ let resolver = def.resolver(db.upcast()); let (impl_trait_lowering, param_lowering) = match def { GenericDefId::FunctionId(_) => { @@ -1714,6 +1731,7 @@ pub(crate) fn generic_predicates_query( let mut predicates = resolver .where_predicates_in_scope() + .filter(|(pred, def)| filter(pred, def)) .flat_map(|(pred, def)| { ctx.lower_where_predicate(pred, def, false).map(|p| make_binders(db, &generics, p)) }) @@ -1747,21 +1765,7 @@ fn implicitly_sized_clauses<'a, 'subst: 'a>( .lang_item(resolver.krate(), LangItem::Sized) .and_then(|lang_item| lang_item.as_trait().map(to_chalk_trait_id))?; - let get_trait_self_idx = |container: ItemContainerId| { - if matches!(container, ItemContainerId::TraitId(_)) { - let generics = generics(db.upcast(), def); - Some(generics.len_self()) - } else { - None - } - }; - let trait_self_idx = match def { - GenericDefId::TraitId(_) => Some(0), - GenericDefId::FunctionId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::ConstId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - GenericDefId::TypeAliasId(it) => get_trait_self_idx(it.lookup(db.upcast()).container), - _ => None, - }; + let trait_self_idx = trait_self_param_idx(db.upcast(), def); Some( substitution @@ -2117,7 +2121,7 @@ pub(crate) fn impl_trait_query(db: &dyn HirDatabase, impl_id: ImplId) -> Option< .with_type_param_mode(ParamLoweringMode::Variable); let (self_ty, binders) = db.impl_self_ty(impl_id).into_value_and_skipped_binders(); let target_trait = impl_data.target_trait.as_ref()?; - Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, Some(self_ty))?)) + Some(Binders::new(binders, ctx.lower_trait_ref(target_trait, self_ty)?)) } pub(crate) fn return_type_impl_traits( diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs index 22d4da0e755a..8e815aabf207 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs @@ -185,8 +185,8 @@ impl ProjectionElem { never!("Out of bound tuple field"); TyKind::Error.intern(Interner) }), - _ => { - never!("Only tuple has tuple field"); + ty => { + never!("Only tuple has tuple field: {:?}", ty); TyKind::Error.intern(Interner) } }, @@ -837,7 +837,9 @@ pub enum CastKind { PointerFromExposedAddress, /// All sorts of pointer-to-pointer casts. Note that reference-to-raw-ptr casts are /// translated into `&raw mut/const *r`, i.e., they are not actually casts. - Pointer(PointerCast), + PtrToPtr, + /// Pointer related casts that are done by coercions. + PointerCoercion(PointerCast), /// Cast into a dyn* object. DynStar, IntToInt, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs index 1bb0c1886857..0d42617d185c 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs @@ -421,9 +421,25 @@ impl MirEvalError { } MirEvalError::MirLowerError(func, err) => { let function_name = db.function_data(*func); + let self_ = match func.lookup(db.upcast()).container { + ItemContainerId::ImplId(impl_id) => Some({ + let generics = crate::generics::generics(db.upcast(), impl_id.into()); + let substs = generics.placeholder_subst(db); + db.impl_self_ty(impl_id) + .substitute(Interner, &substs) + .display(db, edition) + .to_string() + }), + ItemContainerId::TraitId(it) => { + Some(db.trait_data(it).name.display(db.upcast(), edition).to_string()) + } + _ => None, + }; writeln!( f, - "MIR lowering for function `{}` ({:?}) failed due:", + "MIR lowering for function `{}{}{}` ({:?}) failed due:", + self_.as_deref().unwrap_or_default(), + if self_.is_some() { "::" } else { "" }, function_name.name.display(db.upcast(), edition), func )?; @@ -1475,7 +1491,7 @@ impl Evaluator<'_> { } } Rvalue::Cast(kind, operand, target_ty) => match kind { - CastKind::Pointer(cast) => match cast { + CastKind::PointerCoercion(cast) => match cast { PointerCast::ReifyFnPointer | PointerCast::ClosureFnPointer(_) => { let current_ty = self.operand_ty(operand, locals)?; if let TyKind::FnDef(_, _) | TyKind::Closure(_, _) = @@ -1506,6 +1522,7 @@ impl Evaluator<'_> { }, CastKind::DynStar => not_supported!("dyn star cast"), CastKind::IntToInt + | CastKind::PtrToPtr | CastKind::PointerExposeAddress | CastKind::PointerFromExposedAddress => { let current_ty = self.operand_ty(operand, locals)?; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs index 371a5278dc9a..595a78da10fb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval/tests.rs @@ -399,7 +399,7 @@ extern "C" { fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32; } -fn my_cmp(x: &[u8], y: &[u8]) -> i32 { +fn my_cmp(x: &[u8; 3], y: &[u8; 3]) -> i32 { memcmp(x as *const u8, y as *const u8, x.len()) } @@ -779,6 +779,7 @@ fn main() { fn posix_getenv() { check_pass( r#" +//- minicore: sized //- /main.rs env:foo=bar type c_char = u8; @@ -849,7 +850,7 @@ fn main() { fn regression_14966() { check_pass( r#" -//- minicore: fn, copy, coerce_unsized +//- minicore: fn, copy, coerce_unsized, dispatch_from_dyn trait A { fn a(&self) {} } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs index 9e235504519e..a2cb122c5439 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs @@ -31,7 +31,7 @@ use crate::{ display::HirDisplay, error_lifetime, generics::generics, - infer::{CaptureKind, CapturedItem, TypeMismatch}, + infer::{cast::CastTy, unify::InferenceTable, CaptureKind, CapturedItem, TypeMismatch}, inhabitedness::is_ty_uninhabited_from, layout::LayoutError, mapping::ToChalk, @@ -94,7 +94,8 @@ pub enum MirLowerError { UnresolvedField, UnsizedTemporary(Ty), MissingFunctionDefinition(DefWithBodyId, ExprId), - TypeMismatch(Option), + TypeMismatch(TypeMismatch), + HasErrors, /// This should never happen. Type mismatch should catch everything. TypeError(&'static str), NotSupported(String), @@ -179,15 +180,13 @@ impl MirLowerError { body.pretty_print_expr(db.upcast(), *owner, *it, edition) )?; } - MirLowerError::TypeMismatch(e) => match e { - Some(e) => writeln!( - f, - "Type mismatch: Expected {}, found {}", - e.expected.display(db, edition), - e.actual.display(db, edition), - )?, - None => writeln!(f, "Type mismatch: types mismatch with {{unknown}}",)?, - }, + MirLowerError::HasErrors => writeln!(f, "Type inference result contains errors")?, + MirLowerError::TypeMismatch(e) => writeln!( + f, + "Type mismatch: Expected {}, found {}", + e.expected.display(db, edition), + e.actual.display(db, edition), + )?, MirLowerError::GenericArgNotProvided(id, subst) => { let parent = id.parent; let param = &db.generic_params(parent)[id.local_id]; @@ -362,7 +361,7 @@ impl<'ctx> MirLowerCtx<'ctx> { current, place, Rvalue::Cast( - CastKind::Pointer(*cast), + CastKind::PointerCoercion(*cast), Operand::Copy(p), last.target.clone(), ), @@ -898,14 +897,26 @@ impl<'ctx> MirLowerCtx<'ctx> { let Some((it, current)) = self.lower_expr_to_some_operand(*expr, current)? else { return Ok(None); }; - let source_ty = self.infer[*expr].clone(); - let target_ty = self.infer[expr_id].clone(); - self.push_assignment( - current, - place, - Rvalue::Cast(cast_kind(&source_ty, &target_ty)?, it, target_ty), - expr_id.into(), - ); + // Since we don't have THIR, this is the "zipped" version of [rustc's HIR lowering](https://github.com/rust-lang/rust/blob/e71f9529121ca8f687e4b725e3c9adc3f1ebab4d/compiler/rustc_mir_build/src/thir/cx/expr.rs#L165-L178) + // and [THIR lowering as RValue](https://github.com/rust-lang/rust/blob/a4601859ae3875732797873612d424976d9e3dd0/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs#L193-L313) + let rvalue = if self.infer.coercion_casts.contains(expr) { + Rvalue::Use(it) + } else { + let source_ty = self.infer[*expr].clone(); + let target_ty = self.infer[expr_id].clone(); + let cast_kind = if source_ty.as_reference().is_some() { + CastKind::PointerCoercion(PointerCast::ArrayToPointer) + } else { + let mut table = InferenceTable::new( + self.db, + self.db.trait_environment_for_body(self.owner), + ); + cast_kind(&mut table, &source_ty, &target_ty)? + }; + + Rvalue::Cast(cast_kind, it, target_ty) + }; + self.push_assignment(current, place, rvalue, expr_id.into()); Ok(Some(current)) } Expr::Ref { expr, rawness: _, mutability } => { @@ -2005,40 +2016,21 @@ impl<'ctx> MirLowerCtx<'ctx> { } } -fn cast_kind(source_ty: &Ty, target_ty: &Ty) -> Result { - Ok(match (source_ty.kind(Interner), target_ty.kind(Interner)) { - (TyKind::FnDef(..), TyKind::Function(_)) => CastKind::Pointer(PointerCast::ReifyFnPointer), - (TyKind::Scalar(s), TyKind::Scalar(t)) => match (s, t) { - (chalk_ir::Scalar::Float(_), chalk_ir::Scalar::Float(_)) => CastKind::FloatToFloat, - (chalk_ir::Scalar::Float(_), _) => CastKind::FloatToInt, - (_, chalk_ir::Scalar::Float(_)) => CastKind::IntToFloat, - (_, _) => CastKind::IntToInt, - }, - (TyKind::Scalar(_), TyKind::Raw(..)) => CastKind::PointerFromExposedAddress, - (TyKind::Raw(..), TyKind::Scalar(_)) => CastKind::PointerExposeAddress, - (TyKind::Raw(_, a) | TyKind::Ref(_, _, a), TyKind::Raw(_, b) | TyKind::Ref(_, _, b)) => { - CastKind::Pointer(if a == b { - PointerCast::MutToConstPointer - } else if matches!(b.kind(Interner), TyKind::Slice(_)) - && matches!(a.kind(Interner), TyKind::Array(_, _)) - || matches!(b.kind(Interner), TyKind::Dyn(_)) - { - PointerCast::Unsize - } else if matches!(a.kind(Interner), TyKind::Slice(s) if s == b) { - PointerCast::ArrayToPointer - } else { - // cast between two sized pointer, like *const i32 to *const i8, or two unsized pointer, like - // slice to slice, slice to str, ... . These are no-ops (even in the unsized case, no metadata - // will be touched) but there is no specific variant - // for it in `PointerCast` so we use `MutToConstPointer` - PointerCast::MutToConstPointer - }) +fn cast_kind(table: &mut InferenceTable<'_>, source_ty: &Ty, target_ty: &Ty) -> Result { + let from = CastTy::from_ty(table, source_ty); + let cast = CastTy::from_ty(table, target_ty); + Ok(match (from, cast) { + (Some(CastTy::Ptr(..) | CastTy::FnPtr), Some(CastTy::Int(_))) => { + CastKind::PointerExposeAddress } - // Enum to int casts - (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { - CastKind::IntToInt - } - (a, b) => not_supported!("Unknown cast between {a:?} and {b:?}"), + (Some(CastTy::Int(_)), Some(CastTy::Ptr(..))) => CastKind::PointerFromExposedAddress, + (Some(CastTy::Int(_)), Some(CastTy::Int(_))) => CastKind::IntToInt, + (Some(CastTy::FnPtr), Some(CastTy::Ptr(..))) => CastKind::FnPtrToPtr, + (Some(CastTy::Float), Some(CastTy::Int(_))) => CastKind::FloatToInt, + (Some(CastTy::Int(_)), Some(CastTy::Float)) => CastKind::IntToFloat, + (Some(CastTy::Float), Some(CastTy::Float)) => CastKind::FloatToFloat, + (Some(CastTy::Ptr(..)), Some(CastTy::Ptr(..))) => CastKind::PtrToPtr, + _ => not_supported!("Unknown cast between {source_ty:?} and {target_ty:?}"), }) } @@ -2191,7 +2183,7 @@ pub fn lower_to_mir( root_expr: ExprId, ) -> Result { if infer.has_errors { - return Err(MirLowerError::TypeMismatch(None)); + return Err(MirLowerError::HasErrors); } let mut ctx = MirLowerCtx::new(db, owner, body, infer); // 0 is return local diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs new file mode 100644 index 000000000000..a4c662685552 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs @@ -0,0 +1,612 @@ +//! Compute the object-safety of a trait + +use std::ops::ControlFlow; + +use chalk_ir::{ + cast::Cast, + visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}, + DebruijnIndex, +}; +use chalk_solve::rust_ir::InlineBound; +use hir_def::{ + lang_item::LangItem, AssocItemId, ConstId, FunctionId, GenericDefId, HasModule, TraitId, + TypeAliasId, +}; +use rustc_hash::FxHashSet; +use smallvec::SmallVec; + +use crate::{ + all_super_traits, + db::HirDatabase, + from_assoc_type_id, from_chalk_trait_id, + generics::{generics, trait_self_param_idx}, + lower::callable_item_sig, + to_assoc_type_id, to_chalk_trait_id, + utils::elaborate_clause_supertraits, + AliasEq, AliasTy, Binders, BoundVar, CallableSig, GoalData, ImplTraitId, Interner, OpaqueTyId, + ProjectionTyExt, Solution, Substitution, TraitRef, Ty, TyKind, WhereClause, +}; + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum ObjectSafetyViolation { + SizedSelf, + SelfReferential, + Method(FunctionId, MethodViolationCode), + AssocConst(ConstId), + GAT(TypeAliasId), + // This doesn't exist in rustc, but added for better visualization + HasNonSafeSuperTrait(TraitId), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum MethodViolationCode { + StaticMethod, + ReferencesSelfInput, + ReferencesSelfOutput, + ReferencesImplTraitInTrait, + AsyncFn, + WhereClauseReferencesSelf, + Generic, + UndispatchableReceiver, +} + +pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option { + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait)); + } + } + + db.object_safety_of_trait(trait_) +} + +pub fn object_safety_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() { + if db.object_safety_of_trait(super_trait).is_some() { + cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?; + } + } + + object_safety_of_trait_with_callback(db, trait_, cb) +} + +pub fn object_safety_of_trait_with_callback( + db: &dyn HirDatabase, + trait_: TraitId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Check whether this has a `Sized` bound + if generics_require_sized_self(db, trait_.into()) { + cb(ObjectSafetyViolation::SizedSelf)?; + } + + // Check if there exist bounds that referencing self + if predicates_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + if bounds_reference_self(db, trait_) { + cb(ObjectSafetyViolation::SelfReferential)?; + } + + // rustc checks for non-lifetime binders here, but we don't support HRTB yet + + let trait_data = db.trait_data(trait_); + for (_, assoc_item) in &trait_data.items { + object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?; + } + + ControlFlow::Continue(()) +} + +pub fn object_safety_of_trait_query( + db: &dyn HirDatabase, + trait_: TraitId, +) -> Option { + let mut res = None; + object_safety_of_trait_with_callback(db, trait_, &mut |osv| { + res = Some(osv); + ControlFlow::Break(()) + }); + + res +} + +fn generics_require_sized_self(db: &dyn HirDatabase, def: GenericDefId) -> bool { + let krate = def.module(db.upcast()).krate(); + let Some(sized) = db.lang_item(krate, LangItem::Sized).and_then(|l| l.as_trait()) else { + return false; + }; + + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + + let predicates = &*db.generic_predicates(def); + let predicates = predicates.iter().map(|p| p.skip_binders().skip_binders().clone()); + elaborate_clause_supertraits(db, predicates).any(|pred| match pred { + WhereClause::Implemented(trait_ref) => { + if from_chalk_trait_id(trait_ref.trait_id) == sized { + if let TyKind::BoundVar(it) = + *trait_ref.self_type_parameter(Interner).kind(Interner) + { + // Since `generic_predicates` is `Binder>`, the `DebrujinIndex` of + // self-parameter is `1` + return it + .index_if_bound_at(DebruijnIndex::ONE) + .is_some_and(|idx| idx == trait_self_param_idx); + } + } + false + } + _ => false, + }) +} + +// rustc gathers all the spans that references `Self` for error rendering, +// but we don't have good way to render such locations. +// So, just return single boolean value for existence of such `Self` reference +fn predicates_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + db.generic_predicates(trait_.into()) + .iter() + .any(|pred| predicate_references_self(db, trait_, pred, AllowSelfProjection::No)) +} + +// Same as the above, `predicates_reference_self` +fn bounds_reference_self(db: &dyn HirDatabase, trait_: TraitId) -> bool { + let trait_data = db.trait_data(trait_); + trait_data + .items + .iter() + .filter_map(|(_, it)| match *it { + AssocItemId::TypeAliasId(id) => { + let assoc_ty_id = to_assoc_type_id(id); + let assoc_ty_data = db.associated_ty_data(assoc_ty_id); + Some(assoc_ty_data) + } + _ => None, + }) + .any(|assoc_ty_data| { + assoc_ty_data.binders.skip_binders().bounds.iter().any(|bound| { + let def = from_assoc_type_id(assoc_ty_data.id).into(); + match bound.skip_binders() { + InlineBound::TraitBound(it) => it.args_no_self.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + InlineBound::AliasEqBound(it) => it.parameters.iter().any(|arg| { + contains_illegal_self_type_reference( + db, + def, + trait_, + arg, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) + }), + } + }) + }) +} + +#[derive(Clone, Copy)] +enum AllowSelfProjection { + Yes, + No, +} + +fn predicate_references_self( + db: &dyn HirDatabase, + trait_: TraitId, + predicate: &Binders>, + allow_self_projection: AllowSelfProjection, +) -> bool { + match predicate.skip_binders().skip_binders() { + WhereClause::Implemented(trait_ref) => { + trait_ref.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + WhereClause::AliasEq(AliasEq { alias: AliasTy::Projection(proj), .. }) => { + proj.substitution.iter(Interner).skip(1).any(|arg| { + contains_illegal_self_type_reference( + db, + trait_.into(), + trait_, + arg, + DebruijnIndex::ONE, + allow_self_projection, + ) + }) + } + _ => false, + } +} + +fn contains_illegal_self_type_reference>( + db: &dyn HirDatabase, + def: GenericDefId, + trait_: TraitId, + t: &T, + outer_binder: DebruijnIndex, + allow_self_projection: AllowSelfProjection, +) -> bool { + let Some(trait_self_param_idx) = trait_self_param_idx(db.upcast(), def) else { + return false; + }; + struct IllegalSelfTypeVisitor<'a> { + db: &'a dyn HirDatabase, + trait_: TraitId, + super_traits: Option>, + trait_self_param_idx: usize, + allow_self_projection: AllowSelfProjection, + } + impl<'a> TypeVisitor for IllegalSelfTypeVisitor<'a> { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + match ty.kind(Interner) { + TyKind::BoundVar(BoundVar { debruijn, index }) => { + if *debruijn == outer_binder && *index == self.trait_self_param_idx { + ControlFlow::Break(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + TyKind::Alias(AliasTy::Projection(proj)) => match self.allow_self_projection { + AllowSelfProjection::Yes => { + let trait_ = proj.trait_(self.db); + if self.super_traits.is_none() { + self.super_traits = + Some(all_super_traits(self.db.upcast(), self.trait_)); + } + if self.super_traits.as_ref().is_some_and(|s| s.contains(&trait_)) { + ControlFlow::Continue(()) + } else { + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + AllowSelfProjection::No => ty.super_visit_with(self.as_dyn(), outer_binder), + }, + _ => ty.super_visit_with(self.as_dyn(), outer_binder), + } + } + + fn visit_const( + &mut self, + constant: &chalk_ir::Const, + outer_binder: DebruijnIndex, + ) -> std::ops::ControlFlow { + constant.data(Interner).ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let mut visitor = IllegalSelfTypeVisitor { + db, + trait_, + super_traits: None, + trait_self_param_idx, + allow_self_projection, + }; + t.visit_with(visitor.as_dyn(), outer_binder).is_break() +} + +fn object_safety_violation_for_assoc_item( + db: &dyn HirDatabase, + trait_: TraitId, + item: AssocItemId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>, +{ + // Any item that has a `Self : Sized` requisite is otherwise + // exempt from the regulations. + if generics_require_sized_self(db, item.into()) { + return ControlFlow::Continue(()); + } + + match item { + AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)), + AssocItemId::FunctionId(it) => { + virtual_call_violations_for_method(db, trait_, it, &mut |mvc| { + cb(ObjectSafetyViolation::Method(it, mvc)) + }) + } + AssocItemId::TypeAliasId(it) => { + let def_map = db.crate_def_map(trait_.krate(db.upcast())); + if def_map.is_unstable_feature_enabled(&intern::sym::generic_associated_type_extended) { + ControlFlow::Continue(()) + } else { + let generic_params = db.generic_params(item.into()); + if !generic_params.is_empty() { + cb(ObjectSafetyViolation::GAT(it)) + } else { + ControlFlow::Continue(()) + } + } + } + } +} + +fn virtual_call_violations_for_method( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + cb: &mut F, +) -> ControlFlow<()> +where + F: FnMut(MethodViolationCode) -> ControlFlow<()>, +{ + let func_data = db.function_data(func); + if !func_data.has_self_param() { + cb(MethodViolationCode::StaticMethod)?; + } + + if func_data.is_async() { + cb(MethodViolationCode::AsyncFn)?; + } + + let sig = callable_item_sig(db, func.into()); + if sig.skip_binders().params().iter().skip(1).any(|ty| { + contains_illegal_self_type_reference( + db, + func.into(), + trait_, + ty, + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) + }) { + cb(MethodViolationCode::ReferencesSelfInput)?; + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + sig.skip_binders().ret(), + DebruijnIndex::INNERMOST, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::ReferencesSelfOutput)?; + } + + if !func_data.is_async() { + if let Some(mvc) = contains_illegal_impl_trait_in_trait(db, &sig) { + cb(mvc)?; + } + } + + let generic_params = db.generic_params(func.into()); + if generic_params.len_type_or_consts() > 0 { + cb(MethodViolationCode::Generic)?; + } + + if func_data.has_self_param() && !receiver_is_dispatchable(db, trait_, func, &sig) { + cb(MethodViolationCode::UndispatchableReceiver)?; + } + + let predicates = &*db.generic_predicates_without_parent(func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into()); + for pred in predicates { + let pred = pred.skip_binders().skip_binders(); + + if matches!(pred, WhereClause::TypeOutlives(_)) { + continue; + } + + // Allow `impl AutoTrait` predicates + if let WhereClause::Implemented(TraitRef { trait_id, substitution }) = pred { + let trait_data = db.trait_data(from_chalk_trait_id(*trait_id)); + if trait_data.is_auto + && substitution + .as_slice(Interner) + .first() + .and_then(|arg| arg.ty(Interner)) + .and_then(|ty| ty.bound_var(Interner)) + .is_some_and(|b| { + b.debruijn == DebruijnIndex::ONE && Some(b.index) == trait_self_idx + }) + { + continue; + } + } + + if contains_illegal_self_type_reference( + db, + func.into(), + trait_, + pred, + DebruijnIndex::ONE, + AllowSelfProjection::Yes, + ) { + cb(MethodViolationCode::WhereClauseReferencesSelf)?; + break; + } + } + + ControlFlow::Continue(()) +} + +fn receiver_is_dispatchable( + db: &dyn HirDatabase, + trait_: TraitId, + func: FunctionId, + sig: &Binders, +) -> bool { + let Some(trait_self_idx) = trait_self_param_idx(db.upcast(), func.into()) else { + return false; + }; + + // `self: Self` can't be dispatched on, but this is already considered object safe. + // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437 + if sig + .skip_binders() + .params() + .first() + .and_then(|receiver| receiver.bound_var(Interner)) + .is_some_and(|b| { + b == BoundVar { debruijn: DebruijnIndex::INNERMOST, index: trait_self_idx } + }) + { + return true; + } + + let placeholder_subst = generics(db.upcast(), func.into()).placeholder_subst(db); + + let substituted_sig = sig.clone().substitute(Interner, &placeholder_subst); + let Some(receiver_ty) = substituted_sig.params().first() else { + return false; + }; + + let krate = func.module(db.upcast()).krate(); + let traits = ( + db.lang_item(krate, LangItem::Unsize).and_then(|it| it.as_trait()), + db.lang_item(krate, LangItem::DispatchFromDyn).and_then(|it| it.as_trait()), + ); + let (Some(unsize_did), Some(dispatch_from_dyn_did)) = traits else { + return false; + }; + + // Type `U` + let unsized_self_ty = + TyKind::Scalar(chalk_ir::Scalar::Uint(chalk_ir::UintTy::U32)).intern(Interner); + // `Receiver[Self => U]` + let Some(unsized_receiver_ty) = receiver_for_self_ty(db, func, unsized_self_ty.clone()) else { + return false; + }; + + let self_ty = placeholder_subst.as_slice(Interner)[trait_self_idx].assert_ty_ref(Interner); + let unsized_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(unsize_did), + substitution: Substitution::from_iter(Interner, [self_ty.clone(), unsized_self_ty.clone()]), + }); + let trait_predicate = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(trait_), + substitution: Substitution::from_iter( + Interner, + std::iter::once(unsized_self_ty.clone().cast(Interner)) + .chain(placeholder_subst.iter(Interner).skip(1).cloned()), + ), + }); + + let generic_predicates = &*db.generic_predicates(func.into()); + + let clauses = std::iter::once(unsized_predicate) + .chain(std::iter::once(trait_predicate)) + .chain(generic_predicates.iter().map(|pred| { + pred.clone().substitute(Interner, &placeholder_subst).into_value_and_skipped_binders().0 + })) + .map(|pred| { + pred.cast::>(Interner).into_from_env_clause(Interner) + }); + let env = chalk_ir::Environment::new(Interner).add_clauses(Interner, clauses); + + let obligation = WhereClause::Implemented(TraitRef { + trait_id: to_chalk_trait_id(dispatch_from_dyn_did), + substitution: Substitution::from_iter(Interner, [receiver_ty.clone(), unsized_receiver_ty]), + }); + let goal = GoalData::DomainGoal(chalk_ir::DomainGoal::Holds(obligation)).intern(Interner); + + let in_env = chalk_ir::InEnvironment::new(&env, goal); + + let mut table = chalk_solve::infer::InferenceTable::::new(); + let canonicalized = table.canonicalize(Interner, in_env); + let solution = db.trait_solve(krate, None, canonicalized.quantified); + + matches!(solution, Some(Solution::Unique(_))) +} + +fn receiver_for_self_ty(db: &dyn HirDatabase, func: FunctionId, ty: Ty) -> Option { + let generics = generics(db.upcast(), func.into()); + let trait_self_idx = trait_self_param_idx(db.upcast(), func.into())?; + let subst = generics.placeholder_subst(db); + let subst = Substitution::from_iter( + Interner, + subst.iter(Interner).enumerate().map(|(idx, arg)| { + if idx == trait_self_idx { + ty.clone().cast(Interner) + } else { + arg.clone() + } + }), + ); + let sig = callable_item_sig(db, func.into()); + let sig = sig.substitute(Interner, &subst); + sig.params_and_return.first().cloned() +} + +fn contains_illegal_impl_trait_in_trait( + db: &dyn HirDatabase, + sig: &Binders, +) -> Option { + struct OpaqueTypeCollector(FxHashSet); + + impl TypeVisitor for OpaqueTypeCollector { + type BreakTy = (); + + fn as_dyn(&mut self) -> &mut dyn TypeVisitor { + self + } + + fn interner(&self) -> Interner { + Interner + } + + fn visit_ty(&mut self, ty: &Ty, outer_binder: DebruijnIndex) -> ControlFlow { + if let TyKind::OpaqueType(opaque_ty_id, _) = ty.kind(Interner) { + self.0.insert(*opaque_ty_id); + } + ty.super_visit_with(self.as_dyn(), outer_binder) + } + } + + let ret = sig.skip_binders().ret(); + let mut visitor = OpaqueTypeCollector(FxHashSet::default()); + ret.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); + + // Since we haven't implemented RPITIT in proper way like rustc yet, + // just check whether `ret` contains RPIT for now + for opaque_ty in visitor.0 { + let impl_trait_id = db.lookup_intern_impl_trait_id(opaque_ty.into()); + if matches!(impl_trait_id, ImplTraitId::ReturnTypeImplTrait(..)) { + return Some(MethodViolationCode::ReferencesImplTraitInTrait); + } + } + + None +} + +#[cfg(test)] +mod tests; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs new file mode 100644 index 000000000000..c2a9117c5be4 --- /dev/null +++ b/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs @@ -0,0 +1,393 @@ +use std::ops::ControlFlow; + +use hir_def::db::DefDatabase; +use rustc_hash::{FxHashMap, FxHashSet}; +use syntax::ToSmolStr; +use test_fixture::WithFixture; + +use crate::{object_safety::object_safety_with_callback, test_db::TestDB}; + +use super::{ + MethodViolationCode::{self, *}, + ObjectSafetyViolation, +}; + +use ObjectSafetyViolationKind::*; + +#[allow(clippy::upper_case_acronyms)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +enum ObjectSafetyViolationKind { + SizedSelf, + SelfReferential, + Method(MethodViolationCode), + AssocConst, + GAT, + HasNonSafeSuperTrait, +} + +fn check_object_safety<'a>( + ra_fixture: &str, + expected: impl IntoIterator)>, +) { + let mut expected: FxHashMap<_, _> = + expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect(); + let (db, file_ids) = TestDB::with_many_files(ra_fixture); + for (trait_id, name) in file_ids.into_iter().flat_map(|file_id| { + let module_id = db.module_for_file(file_id); + let def_map = module_id.def_map(&db); + let scope = &def_map[module_id.local_id].scope; + scope + .declarations() + .filter_map(|def| { + if let hir_def::ModuleDefId::TraitId(trait_id) = def { + let name = + db.trait_data(trait_id).name.display_no_db(file_id.edition()).to_smolstr(); + Some((trait_id, name)) + } else { + None + } + }) + .collect::>() + }) { + let Some(expected) = expected.remove(name.as_str()) else { + continue; + }; + let mut osvs = FxHashSet::default(); + object_safety_with_callback(&db, trait_id, &mut |osv| { + osvs.insert(match osv { + ObjectSafetyViolation::SizedSelf => SizedSelf, + ObjectSafetyViolation::SelfReferential => SelfReferential, + ObjectSafetyViolation::Method(_, mvc) => Method(mvc), + ObjectSafetyViolation::AssocConst(_) => AssocConst, + ObjectSafetyViolation::GAT(_) => GAT, + ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait, + }); + ControlFlow::Continue(()) + }); + assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;"); + } + + let remains: Vec<_> = expected.keys().collect(); + assert!(remains.is_empty(), "Following traits do not exist in the test fixture; {remains:?}"); +} + +#[test] +fn item_bounds_can_reference_self() { + check_object_safety( + r#" +//- minicore: eq +pub trait Foo { + type X: PartialEq; + type Y: PartialEq; + type Z: PartialEq; +} +"#, + [("Foo", vec![])], + ); +} + +#[test] +fn associated_consts() { + check_object_safety( + r#" +trait Bar { + const X: usize; +} +"#, + [("Bar", vec![AssocConst])], + ); +} + +#[test] +fn bounds_reference_self() { + check_object_safety( + r#" +//- minicore: eq +trait X { + type U: PartialEq; +} +"#, + [("X", vec![SelfReferential])], + ); +} + +#[test] +fn by_value_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(self); +} + +trait Baz { + fn baz(self: Self); +} + +trait Quux { + // Legal because of the where clause: + fn baz(self: Self) where Self : Sized; +} +"#, + [("Bar", vec![]), ("Baz", vec![]), ("Quux", vec![])], + ); +} + +#[test] +fn generic_methods() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, t: T); +} + +trait Quux { + fn bar(&self, t: T) + where Self : Sized; +} + +trait Qax { + fn bar<'a>(&self, t: &'a ()); +} +"#, + [("Bar", vec![Method(Generic)]), ("Quux", vec![]), ("Qax", vec![])], + ); +} + +#[test] +fn mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &Self); +} + +trait Baz { + fn baz(&self) -> Self; +} + +trait Quux { + fn quux(&self, s: &Self) -> Self where Self : Sized; +} +"#, + [ + ("Bar", vec![Method(ReferencesSelfInput)]), + ("Baz", vec![Method(ReferencesSelfOutput)]), + ("Quux", vec![]), + ], + ); +} + +#[test] +fn no_static() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo() {} +} +"#, + [("Foo", vec![Method(StaticMethod)])], + ); +} + +#[test] +fn sized_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar: Sized { + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); + + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar + where Self : Sized +{ + fn bar(&self, t: T); +} +"#, + [("Bar", vec![SizedSelf])], + ); +} + +#[test] +fn supertrait_gat() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait GatTrait { + type Gat; +} + +trait SuperTrait: GatTrait {} +"#, + [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn supertrait_mentions_self() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Bar { + fn bar(&self, x: &T); +} + +trait Baz : Bar { +} +"#, + [("Bar", vec![]), ("Baz", vec![SizedSelf, SelfReferential])], + ); +} + +#[test] +fn rustc_issue_19538() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + fn foo(&self, val: T); +} + +trait Bar: Foo {} +"#, + [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])], + ); +} + +#[test] +fn rustc_issue_22040() { + check_object_safety( + r#" +//- minicore: fmt, eq, dispatch_from_dyn +use core::fmt::Debug; + +trait Expr: Debug + PartialEq { + fn print_element_count(&self); +} +"#, + [("Expr", vec![SelfReferential])], + ); +} + +#[test] +fn rustc_issue_102762() { + check_object_safety( + r#" +//- minicore: future, send, sync, dispatch_from_dyn, deref +use core::pin::Pin; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +struct Vec {} + +pub trait Fetcher: Send + Sync { + fn get<'a>(self: &'a Box) -> Pin> + 'a>> + where + Self: Sync, + { + loop {} + } +} +"#, + [("Fetcher", vec![Method(UndispatchableReceiver)])], + ); +} + +#[test] +fn rustc_issue_102933() { + check_object_safety( + r#" +//- minicore: future, dispatch_from_dyn, deref +use core::future::Future; + +struct Box {} +impl core::ops::Deref for Box { + type Target = T; + + fn deref(&self) -> &Self::Target { + loop {} + } +} +impl, U: ?Sized> DispatchFromDyn> for Box {} + +pub trait Service { + type Response; + type Future: Future; +} + +pub trait A1: Service {} + +pub trait A2: Service>> + A1 { + fn foo(&self) {} +} + +pub trait B1: Service>> {} + +pub trait B2: Service + B1 { + fn foo(&self) {} +} + "#, + [("A2", vec![]), ("B2", vec![])], + ); +} + +#[test] +fn rustc_issue_106247() { + check_object_safety( + r#" +//- minicore: sync, dispatch_from_dyn +pub trait Trait { + fn method(&self) where Self: Sync; +} +"#, + [("Trait", vec![])], + ); +} + +#[test] +fn std_error_is_object_safe() { + check_object_safety( + r#" +//- minicore: fmt, dispatch_from_dyn +trait Erased<'a>: 'a {} + +pub struct Request<'a>(dyn Erased<'a> + 'a); + +pub trait Error: core::fmt::Debug + core::fmt::Display { + fn provide<'a>(&'a self, request: &mut Request<'a>); +} +"#, + [("Error", vec![])], + ); +} + +#[test] +fn lifetime_gat_is_object_unsafe() { + check_object_safety( + r#" +//- minicore: dispatch_from_dyn +trait Foo { + type Bar<'a>; +} +"#, + [("Foo", vec![ObjectSafetyViolationKind::GAT])], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs index 908bbc248c60..273571901ad5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/coercion.rs @@ -49,7 +49,7 @@ fn let_stmt_coerce() { //- minicore: coerce_unsized fn test() { let x: &[isize] = &[1]; - // ^^^^ adjustments: Deref(None), Borrow(Ref('?3, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?2, Not)), Pointer(Unsize) let x: *const [isize] = &[1]; // ^^^^ adjustments: Deref(None), Borrow(RawPtr(Not)), Pointer(Unsize) } @@ -148,7 +148,7 @@ fn foo(x: &[T]) -> &[T] { x } fn test(i: i32) { let x = match i { 2 => foo(&[2]), - // ^^^^ adjustments: Deref(None), Borrow(Ref('?10, Not)), Pointer(Unsize) + // ^^^^ adjustments: Deref(None), Borrow(Ref('?8, Not)), Pointer(Unsize) 1 => &[1], _ => &[3], }; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs index 5454a496ba8c..53b69c12f05d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/macros.rs @@ -1,7 +1,7 @@ use expect_test::expect; use test_utils::{bench, bench_fixture, skip_slow_tests}; -use crate::tests::check_infer_with_mismatches; +use crate::tests::{check_infer_with_mismatches, check_no_mismatches}; use super::{check_infer, check_types}; @@ -206,6 +206,7 @@ fn expr_macro_def_expanded_in_various_places() { 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () 100..119 'for _ ...!() {}': () + 100..119 'for _ ...!() {}': () 104..105 '_': IntoIterator::Item 117..119 '{}': () 124..134 '|| spam!()': impl Fn() -> isize @@ -299,6 +300,7 @@ fn expr_macro_rules_expanded_in_various_places() { 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () 114..133 'for _ ...!() {}': () + 114..133 'for _ ...!() {}': () 118..119 '_': IntoIterator::Item 131..133 '{}': () 138..148 '|| spam!()': impl Fn() -> isize @@ -1404,3 +1406,105 @@ fn foo(t: Tensor) { "#, ); } + +#[test] +fn asm_unit() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() { + core::arch::asm!("") +} +"#, + ); +} + +#[test] +fn asm_no_return() { + check_no_mismatches( + r#" +//- minicore: asm +fn unit() -> ! { + core::arch::asm!("", options(noreturn)) +} +"#, + ); +} + +#[test] +fn asm_things() { + check_infer( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + core::arch::asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", bar, " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + o + + let thread_id: usize; + core::arch::asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + core::arch::asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +"#, + expect![[r#" + !0..122 'builti...muto,)': () + !0..136 'builti...tack))': () + !0..449 'builti...urn),)': ! + 10..1236 '{ ... } }': () + 16..1234 'unsafe... }': () + 37..40 'foo': i32 + 43..44 '1': i32 + 58..63 'mut o': i32 + 66..67 '0': i32 + !95..104 'thread_id': usize + !103..107 '&foo': &'? i32 + !104..107 'foo': i32 + !115..120 '&muto': &'? mut i32 + !119..120 'o': i32 + 293..294 'o': i32 + 308..317 'thread_id': usize + "#]], + ) +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs index 610fc9b6b456..74acf23b75ab 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/method_resolution.rs @@ -1286,6 +1286,7 @@ fn main() { fn method_on_dyn_impl() { check_types( r#" +//- minicore: coerce_unsized trait Foo {} impl Foo for u32 {} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs index 0ccbcf63e2b6..5c63cd00f97d 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs @@ -371,6 +371,7 @@ fn diverging_expression_3_break() { 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () 151..172 'for a ...eak; }': () + 151..172 'for a ...eak; }': () 155..156 'a': {unknown} 160..161 'b': {unknown} 162..172 '{ break; }': () @@ -387,6 +388,7 @@ fn diverging_expression_3_break() { 237..250 'for a in b {}': () 237..250 'for a in b {}': () 237..250 'for a in b {}': () + 237..250 'for a in b {}': () 241..242 'a': {unknown} 246..247 'b': {unknown} 248..250 '{}': () @@ -402,6 +404,7 @@ fn diverging_expression_3_break() { 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () 315..337 'for a ...urn; }': () + 315..337 'for a ...urn; }': () 319..320 'a': {unknown} 324..325 'b': {unknown} 326..337 '{ return; }': () diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs index 57866acc0637..51c27f8714ac 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs @@ -57,6 +57,7 @@ fn infer_pattern() { 101..151 'for (e... }': () 101..151 'for (e... }': () 101..151 'for (e... }': () + 101..151 'for (e... }': () 105..111 '(e, f)': ({unknown}, {unknown}) 106..107 'e': {unknown} 109..110 'f': {unknown} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs index 17fbe4db3179..a3cf12d8a16e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs @@ -275,6 +275,7 @@ fn infer_std_crash_5() { 32..320 'for co... }': () 32..320 'for co... }': () 32..320 'for co... }': () + 32..320 'for co... }': () 36..43 'content': {unknown} 47..60 'doesnt_matter': {unknown} 61..320 '{ ... }': () @@ -1065,7 +1066,7 @@ fn test() { fn bare_dyn_trait_binders_9639() { check_no_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn fn infix_parse(_state: S, _level_code: &Fn(S)) -> T { loop {} } @@ -1244,6 +1245,7 @@ fn test() { 16..66 'for _ ... }': () 16..66 'for _ ... }': () 16..66 'for _ ... }': () + 16..66 'for _ ... }': () 20..21 '_': IntoIterator::Item<()> 25..39 '{ let x = 0; }': () 31..32 'x': i32 @@ -1907,6 +1909,7 @@ fn dont_unify_on_casts() { // #15246 check_types( r#" +//- minicore: sized fn unify(_: [bool; 1]) {} fn casted(_: *const bool) {} fn default() -> T { loop {} } @@ -1926,6 +1929,7 @@ fn test() { fn rustc_test_issue_52437() { check_types( r#" + //- minicore: sized fn main() { let x = [(); &(&'static: loop { |x| {}; }) as *const _ as usize] //^ [(); _] @@ -2228,3 +2232,66 @@ async fn f() -> Bar {} "#]], ); } + +#[test] +fn issue_18109() { + check_infer( + r#" +//- minicore: option +struct Map(T, U); + +impl Map { + fn new() -> Self { loop {} } + fn get(&self, _: &T) -> Option<&U> { loop {} } +} + +fn test(x: bool) { + let map = Map::new(); + let _ = match x { + true => { + let Some(val) = map.get(&8) else { return }; + *val + } + false => return, + _ => 42, + }; +} +"#, + expect![[r#" + 69..80 '{ loop {} }': Map + 71..78 'loop {}': ! + 76..78 '{}': () + 93..97 'self': &'? Map + 99..100 '_': &'? T + 120..131 '{ loop {} }': Option<&'? U> + 122..129 'loop {}': ! + 127..129 '{}': () + 143..144 'x': bool + 152..354 '{ ... }; }': () + 162..165 'map': Map + 168..176 'Map::new': fn new() -> Map + 168..178 'Map::new()': Map + 188..189 '_': i32 + 192..351 'match ... }': i32 + 198..199 'x': bool + 210..214 'true': bool + 210..214 'true': bool + 218..303 '{ ... }': i32 + 236..245 'Some(val)': Option<&'? i32> + 241..244 'val': &'? i32 + 248..251 'map': Map + 248..259 'map.get(&8)': Option<&'? i32> + 256..258 '&8': &'? i32 + 257..258 '8': i32 + 265..275 '{ return }': ! + 267..273 'return': ! + 289..293 '*val': i32 + 290..293 'val': &'? i32 + 312..317 'false': bool + 312..317 'false': bool + 321..327 'return': ! + 337..338 '_': bool + 342..344 '42': i32 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs index 1c6fa62e30ce..0473ee02fab1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs @@ -917,7 +917,7 @@ fn test(a: A) { 278..279 'A': extern "rust-call" A(*mut i32) -> A 278..292 'A(0 as *mut _)': A 278..307 'A(0 as...B(a)))': &'? i32 - 280..281 '0': i32 + 280..281 '0': usize 280..291 '0 as *mut _': *mut i32 297..306 '&&B(B(a))': &'? &'? B>> 298..306 '&B(B(a))': &'? B>> @@ -3572,6 +3572,7 @@ fn f(t: Ark) { fn ref_to_array_to_ptr_cast() { check_types( r#" +//- minicore: sized fn default() -> T { loop {} } fn foo() { let arr = [default()]; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index a98cff2a08b2..0b2d6bdd2593 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -1448,14 +1448,20 @@ fn foo() -> Foo> { fn dyn_trait() { check_infer( r#" -//- minicore: sized +//- minicore: deref, dispatch_from_dyn trait Trait { fn foo(&self) -> T; fn foo2(&self) -> i64; } -fn bar() -> dyn Trait {} -fn test(x: dyn Trait, y: &dyn Trait) { +struct Box {} +impl core::ops::Deref for Box { + type Target = T; +} + +fn bar() -> Box> {} + +fn test(x: Box>, y: &dyn Trait) { x; y; let z = bar(); @@ -1469,27 +1475,27 @@ fn test(x: dyn Trait, y: &dyn Trait) { expect![[r#" 29..33 'self': &'? Self 54..58 'self': &'? Self - 97..99 '{}': dyn Trait - 109..110 'x': dyn Trait - 128..129 'y': &'? dyn Trait - 148..265 '{ ...2(); }': () - 154..155 'x': dyn Trait - 161..162 'y': &'? dyn Trait - 172..173 'z': dyn Trait - 176..179 'bar': fn bar() -> dyn Trait - 176..181 'bar()': dyn Trait - 187..188 'x': dyn Trait - 187..194 'x.foo()': u64 - 200..201 'y': &'? dyn Trait - 200..207 'y.foo()': u64 - 213..214 'z': dyn Trait - 213..220 'z.foo()': u64 - 226..227 'x': dyn Trait - 226..234 'x.foo2()': i64 - 240..241 'y': &'? dyn Trait - 240..248 'y.foo2()': i64 - 254..255 'z': dyn Trait - 254..262 'z.foo2()': i64 + 198..200 '{}': Box> + 210..211 'x': Box> + 234..235 'y': &'? dyn Trait + 254..371 '{ ...2(); }': () + 260..261 'x': Box> + 267..268 'y': &'? dyn Trait + 278..279 'z': Box> + 282..285 'bar': fn bar() -> Box> + 282..287 'bar()': Box> + 293..294 'x': Box> + 293..300 'x.foo()': u64 + 306..307 'y': &'? dyn Trait + 306..313 'y.foo()': u64 + 319..320 'z': Box> + 319..326 'z.foo()': u64 + 332..333 'x': Box> + 332..340 'x.foo2()': i64 + 346..347 'y': &'? dyn Trait + 346..354 'y.foo2()': i64 + 360..361 'z': Box> + 360..368 'z.foo2()': i64 "#]], ); } @@ -1534,7 +1540,7 @@ fn test(s: S) { fn dyn_trait_bare() { check_infer( r#" -//- minicore: sized +//- minicore: sized, dispatch_from_dyn trait Trait { fn foo(&self) -> u64; } @@ -1570,7 +1576,7 @@ fn test(x: Trait, y: &Trait) -> u64 { check_infer_with_mismatches( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn foo(&self) {} @@ -3106,7 +3112,7 @@ fn dyn_fn_param_informs_call_site_closure_signature() { cov_mark::check!(dyn_fn_param_informs_call_site_closure_signature); check_types( r#" -//- minicore: fn, coerce_unsized +//- minicore: fn, coerce_unsized, dispatch_from_dyn struct S; impl S { fn inherent(&self) -> u8 { 0 } @@ -3151,7 +3157,7 @@ fn infer_box_fn_arg() { // The type mismatch is because we don't define Unsize and CoerceUnsized check_infer_with_mismatches( r#" -//- minicore: fn, deref, option +//- minicore: fn, deref, option, dispatch_from_dyn #[lang = "owned_box"] pub struct Box { inner: *mut T, diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs index ffb972475f8f..0b3cdb2f3790 100644 --- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs @@ -4,7 +4,9 @@ //! This probably isn't the best way to do this -- ideally, diagnostics should //! be expressed in terms of hir types themselves. pub use hir_ty::diagnostics::{CaseType, IncorrectCase}; -use hir_ty::{db::HirDatabase, diagnostics::BodyValidationDiagnostic, InferenceDiagnostic}; +use hir_ty::{ + db::HirDatabase, diagnostics::BodyValidationDiagnostic, CastError, InferenceDiagnostic, +}; use cfg::{CfgExpr, CfgOptions}; use either::Either; @@ -50,10 +52,12 @@ macro_rules! diagnostics { diagnostics![ AwaitOutsideOfAsync, BreakOutsideOfLoop, + CastToUnsized, ExpectedFunction, InactiveCode, IncoherentImpl, IncorrectCase, + InvalidCast, InvalidDeriveTarget, MacroDefError, MacroError, @@ -254,6 +258,8 @@ pub struct PrivateField { #[derive(Debug)] pub struct MissingUnsafe { pub expr: InFile>, + /// If true, the diagnostics is an `unsafe_op_in_unsafe_fn` lint instead of a hard error. + pub only_lint: bool, } #[derive(Debug)] @@ -364,6 +370,20 @@ pub struct RemoveUnnecessaryElse { pub if_expr: InFile>, } +#[derive(Debug)] +pub struct CastToUnsized { + pub expr: InFile>, + pub cast_ty: Type, +} + +#[derive(Debug)] +pub struct InvalidCast { + pub expr: InFile>, + pub error: CastError, + pub expr_ty: Type, + pub cast_ty: Type, +} + impl AnyDiagnostic { pub(crate) fn body_validation_diagnostic( db: &dyn HirDatabase, @@ -620,6 +640,16 @@ impl AnyDiagnostic { }; MismatchedTupleStructPatArgCount { expr_or_pat, expected, found }.into() } + InferenceDiagnostic::CastToUnsized { expr, cast_ty } => { + let expr = expr_syntax(*expr)?; + CastToUnsized { expr, cast_ty: Type::new(db, def, cast_ty.clone()) }.into() + } + InferenceDiagnostic::InvalidCast { expr, error, expr_ty, cast_ty } => { + let expr = expr_syntax(*expr)?; + let expr_ty = Type::new(db, def, expr_ty.clone()); + let cast_ty = Type::new(db, def, cast_ty.clone()); + InvalidCast { expr, error: *error, expr_ty, cast_ty }.into() + } }) } } diff --git a/src/tools/rust-analyzer/crates/hir/src/display.rs b/src/tools/rust-analyzer/crates/hir/src/display.rs index 923dca646673..c2b2fbef7517 100644 --- a/src/tools/rust-analyzer/crates/hir/src/display.rs +++ b/src/tools/rust-analyzer/crates/hir/src/display.rs @@ -99,17 +99,20 @@ impl HirDisplay for Function { } // FIXME: Use resolved `param.ty` once we no longer discard lifetimes + let body = db.body(self.id.into()); for (type_ref, param) in data.params.iter().zip(self.assoc_fn_params(db)).skip(skip_self) { - let local = param.as_local(db).map(|it| it.name(db)); if !first { f.write_str(", ")?; } else { first = false; } - match local { - Some(name) => write!(f, "{}: ", name.display(f.db.upcast(), f.edition()))?, - None => f.write_str("_: ")?, - } + + let pat_id = body.params[param.idx - body.self_param.is_some() as usize]; + let pat_str = + body.pretty_print_pat(db.upcast(), self.id.into(), pat_id, true, f.edition()); + f.write_str(&pat_str)?; + + f.write_str(": ")?; type_ref.hir_fmt(f)?; } diff --git a/src/tools/rust-analyzer/crates/hir/src/has_source.rs b/src/tools/rust-analyzer/crates/hir/src/has_source.rs index 7d52a28b91e9..82c90ac30101 100644 --- a/src/tools/rust-analyzer/crates/hir/src/has_source.rs +++ b/src/tools/rust-analyzer/crates/hir/src/has_source.rs @@ -14,8 +14,8 @@ use tt::TextRange; use crate::{ db::HirDatabase, Adt, Callee, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl, - Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, Struct, Trait, - TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, + InlineAsmOperand, Label, LifetimeParam, LocalSource, Macro, Module, Param, SelfParam, Static, + Struct, Trait, TraitAlias, TypeAlias, TypeOrConstParam, Union, Variant, }; pub trait HasSource { @@ -292,3 +292,26 @@ impl HasSource for ExternCrateDecl { Some(self.id.lookup(db.upcast()).source(db.upcast())) } } + +impl HasSource for InlineAsmOperand { + type Ast = ast::AsmOperandNamed; + fn source(self, db: &dyn HirDatabase) -> Option> { + let (_body, source_map) = db.body_with_source_map(self.owner); + if let Ok(src) = source_map.expr_syntax(self.expr) { + let root = src.file_syntax(db.upcast()); + return src + .map(|ast| match ast.to_node(&root) { + ast::Expr::AsmExpr(asm) => asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .nth(self.index), + _ => None, + }) + .transpose(); + } + None + } +} diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs index 58340c74fea9..8f5db32f9576 100644 --- a/src/tools/rust-analyzer/crates/hir/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs @@ -43,7 +43,7 @@ use hir_def::{ body::{BodyDiagnostic, SyntheticSyntax}, data::adt::VariantData, generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, - hir::{BindingAnnotation, BindingId, ExprOrPatId, LabelId, Pat}, + hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat}, item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode}, lang_item::LangItemTarget, layout::{self, ReprOptions, TargetDataLayout}, @@ -66,7 +66,7 @@ use hir_ty::{ diagnostics::BodyValidationDiagnostic, error_lifetime, known_const_to_ast, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, - method_resolution::{self}, + method_resolution, mir::{interpret_mir, MutBorrowKind}, primitive::UintTy, traits::FnTrait, @@ -80,7 +80,7 @@ use nameres::diagnostics::DefDiagnosticKind; use rustc_hash::FxHashSet; use smallvec::SmallVec; use span::{Edition, EditionedFileId, FileId, MacroCallId, SyntaxContextId}; -use stdx::{impl_from, never}; +use stdx::{format_to, impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasGenericParams, HasName}, format_smolstr, AstNode, AstPtr, SmolStr, SyntaxNode, SyntaxNodePtr, TextRange, ToSmolStr, T, @@ -137,6 +137,7 @@ pub use { hygiene::{marks_rev, SyntaxContextExt}, inert_attr_macro::AttributeTemplate, name::Name, + prettify_macro_expansion, proc_macro::{ProcMacros, ProcMacrosBuilder}, tt, ExpandResult, HirFileId, HirFileIdExt, MacroFileId, MacroFileIdExt, }, @@ -145,7 +146,8 @@ pub use { display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, layout::LayoutError, mir::{MirEvalError, MirLowerError}, - FnAbi, PointerCast, Safety, + object_safety::{MethodViolationCode, ObjectSafetyViolation}, + CastError, FnAbi, PointerCast, Safety, }, // FIXME: Properly encapsulate mir hir_ty::{mir, Interner as ChalkTyInterner}, @@ -1882,9 +1884,10 @@ impl DefWithBody { ); } - for expr in hir_ty::diagnostics::missing_unsafe(db, self.into()) { + let (unafe_exprs, only_lint) = hir_ty::diagnostics::missing_unsafe(db, self.into()); + for expr in unafe_exprs { match source_map.expr_syntax(expr) { - Ok(expr) => acc.push(MissingUnsafe { expr }.into()), + Ok(expr) => acc.push(MissingUnsafe { expr, only_lint }.into()), Err(SyntheticSyntax) => { // FIXME: Here and elsewhere in this file, the `expr` was // desugared, report or assert that this doesn't happen. @@ -2206,6 +2209,35 @@ impl Function { db.function_data(self.id).is_async() } + pub fn returns_impl_future(self, db: &dyn HirDatabase) -> bool { + if self.is_async(db) { + return true; + } + + let Some(impl_traits) = self.ret_type(db).as_impl_traits(db) else { return false }; + let Some(future_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Future).and_then(|t| t.as_trait()) + else { + return false; + }; + let Some(sized_trait_id) = + db.lang_item(self.ty(db).env.krate, LangItem::Sized).and_then(|t| t.as_trait()) + else { + return false; + }; + + let mut has_impl_future = false; + impl_traits + .filter(|t| { + let fut = t.id == future_trait_id; + has_impl_future |= fut; + !fut && t.id != sized_trait_id + }) + // all traits but the future trait must be auto traits + .all(|t| t.is_auto(db)) + && has_impl_future + } + /// Does this function have `#[test]` attribute? pub fn is_test(self, db: &dyn HirDatabase) -> bool { db.function_data(self.id).attrs.is_test() @@ -2522,6 +2554,17 @@ impl Const { Type::from_value_def(db, self.id) } + /// Evaluate the constant and return the result as a string. + /// + /// This function is intended for IDE assistance, different from [`Const::render_eval`]. + pub fn eval(self, db: &dyn HirDatabase, edition: Edition) -> Result { + let c = db.const_eval(self.id.into(), Substitution::empty(Interner), None)?; + Ok(format!("{}", c.display(db, edition))) + } + + /// Evaluate the constant and return the result as a string, with more detailed information. + /// + /// This function is intended for user-facing display. pub fn render_eval( self, db: &dyn HirDatabase, @@ -2536,10 +2579,16 @@ impl Const { let value = u128::from_le_bytes(mir::pad16(b, false)); let value_signed = i128::from_le_bytes(mir::pad16(b, matches!(s, Scalar::Int(_)))); - if value >= 10 { - return Ok(format!("{value_signed} ({value:#X})")); + let mut result = if let Scalar::Int(_) = s { + value_signed.to_string() } else { - return Ok(format!("{value_signed}")); + value.to_string() + }; + if value >= 10 { + format_to!(result, " ({value:#X})"); + return Ok(result); + } else { + return Ok(result); } } } @@ -2641,6 +2690,10 @@ impl Trait { .count() } + pub fn object_safety(&self, db: &dyn HirDatabase) -> Option { + hir_ty::object_safety::object_safety(db, self.id) + } + fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId, MacroCallId)]> { db.trait_data(self.id) .macro_calls @@ -5211,6 +5264,26 @@ impl Type { } } +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)] +pub struct InlineAsmOperand { + owner: DefWithBodyId, + expr: ExprId, + index: usize, +} + +impl InlineAsmOperand { + pub fn parent(self, _db: &dyn HirDatabase) -> DefWithBody { + self.owner.into() + } + + pub fn name(&self, db: &dyn HirDatabase) -> Option { + match &db.body(self.owner)[self.expr] { + hir_def::hir::Expr::InlineAsm(e) => e.operands.get(self.index)?.0.clone(), + _ => None, + } + } +} + // FIXME: Document this #[derive(Debug)] pub struct Callable { diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index 763f53031e4c..fa14b53dbc3d 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -13,7 +13,8 @@ use either::Either; use hir_def::{ hir::Expr, lower::LowerCtx, - nameres::MacroSubNs, + nameres::{MacroSubNs, ModuleOrigin}, + path::ModPath, resolver::{self, HasResolver, Resolver, TypeNs}, type_ref::Mutability, AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId, @@ -31,7 +32,7 @@ use intern::Symbol; use itertools::Itertools; use rustc_hash::{FxHashMap, FxHashSet}; use smallvec::{smallvec, SmallVec}; -use span::{EditionedFileId, FileId}; +use span::{EditionedFileId, FileId, HirFileIdRepr}; use stdx::TupleExt; use syntax::{ algo::skip_trivia_token, @@ -46,9 +47,9 @@ use crate::{ source_analyzer::{resolve_hir_path, SourceAnalyzer}, Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const, ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile, - Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, - Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, - Variant, VariantDef, + InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, + OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, + Type, TypeAlias, TypeParam, Union, Variant, VariantDef, }; const CONTINUE_NO_BREAKS: ControlFlow = ControlFlow::Continue(()); @@ -322,6 +323,47 @@ impl<'db> SemanticsImpl<'db> { tree } + pub fn find_parent_file(&self, file_id: HirFileId) -> Option> { + match file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + let module = self.file_to_module_defs(file_id.file_id()).next()?; + let def_map = self.db.crate_def_map(module.krate().id); + match def_map[module.id.local_id].origin { + ModuleOrigin::CrateRoot { .. } => None, + ModuleOrigin::File { declaration, declaration_tree_id, .. } => { + let file_id = declaration_tree_id.file_id(); + let in_file = InFile::new(file_id, declaration); + let node = in_file.to_node(self.db.upcast()); + let root = find_root(node.syntax()); + self.cache(root, file_id); + Some(in_file.with_value(node.syntax().clone())) + } + _ => unreachable!("FileId can only belong to a file module"), + } + } + HirFileIdRepr::MacroFile(macro_file) => { + let node = self + .db + .lookup_intern_macro_call(macro_file.macro_call_id) + .to_node(self.db.upcast()); + let root = find_root(&node.value); + self.cache(root, node.file_id); + Some(node) + } + } + } + + /// Returns the `SyntaxNode` of the module. If this is a file module, returns + /// the `SyntaxNode` of the *definition* file, not of the *declaration*. + pub fn module_definition_node(&self, module: Module) -> InFile { + let def_map = module.id.def_map(self.db.upcast()); + let definition = def_map[module.id.local_id].origin.definition_source(self.db.upcast()); + let definition = definition.map(|it| it.node()); + let root_node = find_root(&definition.value); + self.cache(root_node, definition.file_id); + definition + } + pub fn parse_or_expand(&self, file_id: HirFileId) -> SyntaxNode { let node = self.db.parse_or_expand(file_id); self.cache(node.clone(), file_id); @@ -344,6 +386,19 @@ impl<'db> SemanticsImpl<'db> { Some(node) } + pub fn check_cfg_attr(&self, attr: &ast::TokenTree) -> Option { + let file_id = self.find_file(attr.syntax()).file_id; + let krate = match file_id.repr() { + HirFileIdRepr::FileId(file_id) => { + self.file_to_module_defs(file_id.file_id()).next()?.krate().id + } + HirFileIdRepr::MacroFile(macro_file) => { + self.db.lookup_intern_macro_call(macro_file.macro_call_id).krate + } + }; + hir_expand::check_cfg_attr_value(self.db.upcast(), attr, krate) + } + /// Expands the macro if it isn't one of the built-in ones that expand to custom syntax or dummy /// expansions. pub fn expand_allowed_builtins(&self, macro_call: &ast::MacroCall) -> Option { @@ -367,7 +422,6 @@ impl<'db> SemanticsImpl<'db> { | BuiltinFnLikeExpander::File | BuiltinFnLikeExpander::ModulePath | BuiltinFnLikeExpander::Asm - | BuiltinFnLikeExpander::LlvmAsm | BuiltinFnLikeExpander::GlobalAsm | BuiltinFnLikeExpander::LogSyntax | BuiltinFnLikeExpander::TraceMacros @@ -408,7 +462,7 @@ impl<'db> SemanticsImpl<'db> { Some( calls .into_iter() - .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id })) + .map(|call| macro_call_to_macro_id(self, ctx, call?).map(|id| Macro { id })) .collect(), ) }) @@ -546,11 +600,11 @@ impl<'db> SemanticsImpl<'db> { ) } - /// Retrieves all the formatting parts of the format_args! template string. + /// Retrieves all the formatting parts of the format_args! (or `asm!`) template string. pub fn as_format_args_parts( &self, string: &ast::String, - ) -> Option)>> { + ) -> Option>)>> { let quote = string.open_quote_text_range()?; let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?; @@ -560,14 +614,33 @@ impl<'db> SemanticsImpl<'db> { let string = ast::String::cast(token)?; let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - let res = source_analyzer - .as_format_args_parts(self.db, format_args.as_ref())? - .map(|(range, res)| (range + quote.end(), res)) - .collect(); - Some(res) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + let res = source_analyzer + .as_format_args_parts(self.db, format_args.as_ref())? + .map(|(range, res)| (range + quote.end(), res.map(Either::Left))) + .collect(); + Some(res) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; + let asm = self.wrap_node_infile(asm); + let (owner, (expr, asm_parts)) = source_analyzer.as_asm_parts(asm.as_ref())?; + let res = asm_parts + .get(line)? + .iter() + .map(|&(range, index)| { + ( + range + quote.end(), + Some(Either::Right(InlineAsmOperand { owner, expr, index })), + ) + }) + .collect(); + Some(res) + } })() .map_or(ControlFlow::Continue(()), ControlFlow::Break) }) @@ -578,7 +651,7 @@ impl<'db> SemanticsImpl<'db> { &self, original_token: SyntaxToken, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { let original_string = ast::String::cast(original_token.clone())?; let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?; let quote = original_string.open_quote_text_range()?; @@ -599,13 +672,27 @@ impl<'db> SemanticsImpl<'db> { &self, string: ast::String, offset: TextSize, - ) -> Option<(TextRange, Option)> { + ) -> Option<(TextRange, Option>)> { debug_assert!(offset <= string.syntax().text_range().len()); let literal = string.syntax().parent().filter(|it| it.kind() == SyntaxKind::LITERAL)?; - let format_args = ast::FormatArgsExpr::cast(literal.parent()?)?; - let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; - let format_args = self.wrap_node_infile(format_args); - source_analyzer.resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + let parent = literal.parent()?; + if let Some(format_args) = ast::FormatArgsExpr::cast(parent.clone()) { + let source_analyzer = &self.analyze_no_infer(format_args.syntax())?; + let format_args = self.wrap_node_infile(format_args); + source_analyzer + .resolve_offset_in_format_args(self.db, format_args.as_ref(), offset) + .map(|(range, res)| (range, res.map(Either::Left))) + } else { + let asm = ast::AsmExpr::cast(parent)?; + let source_analyzer = &self.analyze_no_infer(asm.syntax())?; + let line = asm.template().position(|it| *it.syntax() == literal)?; + let asm = self.wrap_node_infile(asm); + source_analyzer.resolve_offset_in_asm_template(asm.as_ref(), line, offset).map( + |(owner, (expr, range, index))| { + (range, Some(Either::Right(InlineAsmOperand { owner, expr, index }))) + }, + ) + } } /// Maps a node down by mapping its first and last token down. @@ -818,16 +905,7 @@ impl<'db> SemanticsImpl<'db> { let InMacroFile { file_id, value: mapped_tokens } = self.with_ctx(|ctx| { Some( ctx.cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| { - let exp_info = macro_file.expansion_info(self.db.upcast()); - - let InMacroFile { file_id, value } = exp_info.expanded(); - self.cache(value, file_id.into()); - - exp_info - }) + .get_or_insert_expansion(self, macro_file) .map_range_down(span)? .map(SmallVec::<[_; 2]>::from_iter), ) @@ -1113,11 +1191,7 @@ impl<'db> SemanticsImpl<'db> { let macro_file = file_id.macro_file()?; self.with_ctx(|ctx| { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(self.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(self, macro_file); expansion_info.arg().map(|node| node?.parent()).transpose() }) } @@ -1333,7 +1407,7 @@ impl<'db> SemanticsImpl<'db> { let macro_call = self.find_file(macro_call.syntax()).with_value(macro_call); self.with_ctx(|ctx| { ctx.macro_call_to_macro_call(macro_call) - .and_then(|call| macro_call_to_macro_id(ctx, call)) + .and_then(|call| macro_call_to_macro_id(self, ctx, call)) .map(Into::into) }) .or_else(|| { @@ -1375,7 +1449,7 @@ impl<'db> SemanticsImpl<'db> { let item_in_file = self.wrap_node_infile(item.clone()); let id = self.with_ctx(|ctx| { let macro_call_id = ctx.item_to_macro_call(item_in_file.as_ref())?; - macro_call_to_macro_id(ctx, macro_call_id) + macro_call_to_macro_id(self, ctx, macro_call_id) })?; Some(Macro { id }) } @@ -1384,6 +1458,16 @@ impl<'db> SemanticsImpl<'db> { self.analyze(path.syntax())?.resolve_path(self.db, path) } + pub fn resolve_mod_path( + &self, + scope: &SyntaxNode, + path: &ModPath, + ) -> Option> { + let analyze = self.analyze(scope)?; + let items = analyze.resolver.resolve_module_path_in_items(self.db.upcast(), path); + Some(items.iter_items().map(|(item, _)| item.into())) + } + fn resolve_variant(&self, record_lit: ast::RecordExpr) -> Option { self.analyze(record_lit.syntax())?.resolve_variant(self.db, record_lit) } @@ -1685,6 +1769,7 @@ impl<'db> SemanticsImpl<'db> { } fn macro_call_to_macro_id( + sema: &SemanticsImpl<'_>, ctx: &mut SourceToDefCtx<'_, '_>, macro_call_id: MacroCallId, ) -> Option { @@ -1700,11 +1785,7 @@ fn macro_call_to_macro_id( it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) } HirFileIdRepr::MacroFile(macro_file) => { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file); it.to_ptr(db).to_node(&expansion_info.expanded().value) } }; @@ -1716,11 +1797,7 @@ fn macro_call_to_macro_id( it.to_ptr(db).to_node(&db.parse(file_id).syntax_node()) } HirFileIdRepr::MacroFile(macro_file) => { - let expansion_info = ctx - .cache - .expansion_info_cache - .entry(macro_file) - .or_insert_with(|| macro_file.expansion_info(ctx.db.upcast())); + let expansion_info = ctx.cache.get_or_insert_expansion(sema, macro_file); it.to_ptr(db).to_node(&expansion_info.expanded().value) } }; @@ -1771,6 +1848,7 @@ to_def_impls![ (crate::Label, ast::Label, label_to_def), (crate::Adt, ast::Adt, adt_to_def), (crate::ExternCrateDecl, ast::ExternCrate, extern_crate_to_def), + (crate::InlineAsmOperand, ast::AsmOperandNamed, asm_operand_to_def), (MacroCallId, ast::MacroCall, macro_call_to_macro_call), ]; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 09df639d4a81..c1e4e1d1e271 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -99,7 +99,8 @@ use hir_def::{ VariantId, }; use hir_expand::{ - attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, MacroCallId, + attrs::AttrId, name::AsName, ExpansionInfo, HirFileId, HirFileIdExt, InMacroFile, MacroCallId, + MacroFileIdExt, }; use rustc_hash::FxHashMap; use smallvec::SmallVec; @@ -110,15 +111,32 @@ use syntax::{ AstNode, AstPtr, SyntaxNode, }; -use crate::{db::HirDatabase, InFile}; +use crate::{db::HirDatabase, InFile, InlineAsmOperand, SemanticsImpl}; #[derive(Default)] pub(super) struct SourceToDefCache { pub(super) dynmap_cache: FxHashMap<(ChildContainer, HirFileId), DynMap>, - pub(super) expansion_info_cache: FxHashMap, + expansion_info_cache: FxHashMap, pub(super) file_to_def_cache: FxHashMap>, } +impl SourceToDefCache { + pub(super) fn get_or_insert_expansion( + &mut self, + sema: &SemanticsImpl<'_>, + macro_file: MacroFileId, + ) -> &ExpansionInfo { + self.expansion_info_cache.entry(macro_file).or_insert_with(|| { + let exp_info = macro_file.expansion_info(sema.db.upcast()); + + let InMacroFile { file_id, value } = exp_info.expanded(); + sema.cache(value, file_id.into()); + + exp_info + }) + } +} + pub(super) struct SourceToDefCtx<'db, 'cache> { pub(super) db: &'db dyn HirDatabase, pub(super) cache: &'cache mut SourceToDefCache, @@ -273,6 +291,25 @@ impl SourceToDefCtx<'_, '_> { ast::Adt::Union(it) => self.union_to_def(InFile::new(file_id, it)).map(AdtId::UnionId), } } + + pub(super) fn asm_operand_to_def( + &mut self, + src: InFile<&ast::AsmOperandNamed>, + ) -> Option { + let asm = src.value.syntax().parent().and_then(ast::AsmExpr::cast)?; + let index = asm + .asm_pieces() + .filter_map(|it| match it { + ast::AsmPiece::AsmOperandNamed(it) => Some(it), + _ => None, + }) + .position(|it| it == *src.value)?; + let container = self.find_pat_or_label_container(src.syntax_ref())?; + let (_, source_map) = self.db.body_with_source_map(container); + let expr = source_map.node_expr(src.with_value(&ast::Expr::AsmExpr(asm)))?; + Some(InlineAsmOperand { owner: container, expr, index }) + } + pub(super) fn bind_pat_to_def( &mut self, src: InFile<&ast::IdentPat>, @@ -281,7 +318,7 @@ impl SourceToDefCtx<'_, '_> { let (body, source_map) = self.db.body_with_source_map(container); let src = src.cloned().map(ast::Pat::from); let pat_id = source_map.node_pat(src.as_ref())?; - // the pattern could resolve to a constant, verify that that is not the case + // the pattern could resolve to a constant, verify that this is not the case if let crate::Pat::Bind { id, .. } = body[pat_id] { Some((container, id)) } else { diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs index be0116862b9c..3da67ae23f83 100644 --- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs +++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs @@ -904,6 +904,22 @@ impl SourceAnalyzer { }) } + pub(crate) fn resolve_offset_in_asm_template( + &self, + asm: InFile<&ast::AsmExpr>, + line: usize, + offset: TextSize, + ) -> Option<(DefWithBodyId, (ExprId, TextRange, usize))> { + let (def, _, body_source_map) = self.def.as_ref()?; + let (expr, args) = body_source_map.asm_template_args(asm)?; + Some(*def).zip( + args.get(line)? + .iter() + .find(|(range, _)| range.contains_inclusive(offset)) + .map(|(range, idx)| (expr, *range, *idx)), + ) + } + pub(crate) fn as_format_args_parts<'a>( &'a self, db: &'a dyn HirDatabase, @@ -927,6 +943,14 @@ impl SourceAnalyzer { )) } + pub(crate) fn as_asm_parts( + &self, + asm: InFile<&ast::AsmExpr>, + ) -> Option<(DefWithBodyId, (ExprId, &[Vec<(TextRange, usize)>]))> { + let (def, _, body_source_map) = self.def.as_ref()?; + Some(*def).zip(body_source_map.asm_template_args(asm)) + } + fn resolve_impl_method_or_trait_def( &self, db: &dyn HirDatabase, diff --git a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml index df52562b6a10..2a14fbe1e0a2 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml +++ b/src/tools/rust-analyzer/crates/ide-assists/Cargo.toml @@ -34,5 +34,8 @@ expect-test = "1.4.0" test-utils.workspace = true test-fixture.workspace = true +[features] +in-rust-tree = [] + [lints] workspace = true diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b6abb06a2afc..a211ca8f2d67 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs @@ -1,12 +1,13 @@ use std::iter::{self, Peekable}; use either::Either; -use hir::{sym, Adt, Crate, HasAttrs, HasSource, ImportPathConfig, ModuleDef, Semantics}; +use hir::{sym, Adt, Crate, HasAttrs, ImportPathConfig, ModuleDef, Semantics}; +use ide_db::syntax_helpers::suggest_name; use ide_db::RootDatabase; use ide_db::{famous_defs::FamousDefs, helpers::mod_path_to_ast}; use itertools::Itertools; use syntax::ast::edit_in_place::Removable; -use syntax::ast::{self, make, AstNode, HasName, MatchArmList, MatchExpr, Pat}; +use syntax::ast::{self, make, AstNode, MatchArmList, MatchExpr, Pat}; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; @@ -90,7 +91,7 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) .into_iter() .filter_map(|variant| { Some(( - build_pat(ctx.db(), module, variant, cfg)?, + build_pat(ctx, module, variant, cfg)?, variant.should_be_hidden(ctx.db(), module.krate()), )) }) @@ -141,9 +142,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::tuple_pat(patterns)), is_hidden) }) @@ -174,9 +174,8 @@ pub(crate) fn add_missing_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) let is_hidden = variants .iter() .any(|variant| variant.should_be_hidden(ctx.db(), module.krate())); - let patterns = variants - .into_iter() - .filter_map(|variant| build_pat(ctx.db(), module, variant, cfg)); + let patterns = + variants.into_iter().filter_map(|variant| build_pat(ctx, module, variant, cfg)); (ast::Pat::from(make::slice_pat(patterns)), is_hidden) }) .filter(|(variant_pat, _)| is_variant_missing(&top_lvl_pats, variant_pat)); @@ -438,33 +437,39 @@ fn resolve_array_of_enum_def( } fn build_pat( - db: &RootDatabase, + ctx: &AssistContext<'_>, module: hir::Module, var: ExtendedVariant, cfg: ImportPathConfig, ) -> Option { + let db = ctx.db(); match var { ExtendedVariant::Variant(var) => { let edition = module.krate().edition(db); let path = mod_path_to_ast(&module.find_path(db, ModuleDef::from(var), cfg)?, edition); - // FIXME: use HIR for this; it doesn't currently expose struct vs. tuple vs. unit variants though - Some(match var.source(db)?.value.kind() { - ast::StructKind::Tuple(field_list) => { - let pats = - iter::repeat(make::wildcard_pat().into()).take(field_list.fields().count()); + let fields = var.fields(db); + let pat = match var.kind(db) { + hir::StructKind::Tuple => { + let mut name_generator = suggest_name::NameGenerator::new(); + let pats = fields.into_iter().map(|f| { + let name = name_generator.for_type(&f.ty(db), db, edition); + match name { + Some(name) => make::ext::simple_ident_pat(make::name(&name)).into(), + None => make::wildcard_pat().into(), + } + }); make::tuple_struct_pat(path, pats).into() } - ast::StructKind::Record(field_list) => { - let pats = field_list.fields().map(|f| { - make::ext::simple_ident_pat( - f.name().expect("Record field must have a name"), - ) - .into() - }); + hir::StructKind::Record => { + let pats = fields + .into_iter() + .map(|f| make::name(f.name(db).as_str())) + .map(|name| make::ext::simple_ident_pat(name).into()); make::record_pat(path, pats).into() } - ast::StructKind::Unit => make::path_pat(path), - }) + hir::StructKind::Unit => make::path_pat(path), + }; + Some(pat) } ExtendedVariant::True => Some(ast::Pat::from(make::literal_pat("true"))), ExtendedVariant::False => Some(ast::Pat::from(make::literal_pat("false"))), @@ -1976,4 +1981,81 @@ fn a() { }"#, ) } + + #[test] + fn suggest_name_for_tuple_struct_patterns() { + // single tuple struct + check_assist( + add_missing_match_arms, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S; + +pub enum E { + A + B(S), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s) => todo!(), + } +} +"#, + ); + + // multiple tuple struct patterns + check_assist( + add_missing_match_arms, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0 + } +} +"#, + r#" +struct S1; +struct S2; + +pub enum E { + A + B(S1, S2), +} + +fn f() { + let value = E::A; + match value { + $0E::A => todo!(), + E::B(s1, s2) => todo!(), + } +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs new file mode 100644 index 000000000000..fafc3448a87c --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs @@ -0,0 +1,206 @@ +use hir::Semantics; +use ide_db::{ + assists::{AssistId, AssistKind}, + source_change::SourceChangeBuilder, + RootDatabase, +}; +use syntax::{ast, AstNode}; + +use crate::{AssistContext, Assists}; + +// Assist: explicit_enum_discriminant +// +// Adds explicit discriminant to all enum variants. +// +// ``` +// enum TheEnum$0 { +// Foo, +// Bar, +// Baz = 42, +// Quux, +// } +// ``` +// -> +// ``` +// enum TheEnum { +// Foo = 0, +// Bar = 1, +// Baz = 42, +// Quux = 43, +// } +// ``` +pub(crate) fn explicit_enum_discriminant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let enum_node = ctx.find_node_at_offset::()?; + let enum_def = ctx.sema.to_def(&enum_node)?; + + let is_data_carrying = enum_def.is_data_carrying(ctx.db()); + let has_primitive_repr = enum_def.repr(ctx.db()).and_then(|repr| repr.int).is_some(); + + // Data carrying enums without a primitive repr have no stable discriminants. + if is_data_carrying && !has_primitive_repr { + return None; + } + + let variant_list = enum_node.variant_list()?; + + // Don't offer the assist if the enum has no variants or if all variants already have an + // explicit discriminant. + if variant_list.variants().all(|variant_node| variant_node.expr().is_some()) { + return None; + } + + acc.add( + AssistId("explicit_enum_discriminant", AssistKind::RefactorRewrite), + "Add explicit enum discriminants", + enum_node.syntax().text_range(), + |builder| { + for variant_node in variant_list.variants() { + add_variant_discriminant(&ctx.sema, builder, &variant_node); + } + }, + ); + + Some(()) +} + +fn add_variant_discriminant( + sema: &Semantics<'_, RootDatabase>, + builder: &mut SourceChangeBuilder, + variant_node: &ast::Variant, +) { + if variant_node.expr().is_some() { + return; + } + + let Some(variant_def) = sema.to_def(variant_node) else { + return; + }; + let Ok(discriminant) = variant_def.eval(sema.db) else { + return; + }; + + let variant_range = variant_node.syntax().text_range(); + + builder.insert(variant_range.end(), format!(" = {discriminant}")); +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::explicit_enum_discriminant; + + #[test] + fn non_primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, + FooBar = -5, + FooBaz, +} +"#, + r#" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, + FooBar = -5, + FooBaz = -4, +} +"#, + ); + } + + #[test] + fn primitive_repr_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(u8)] +$0enum TheEnum { + Foo { x: u32 }, + Bar, + Baz(String), + Quux, +} +"#, + r#" +#[repr(u8)] +enum TheEnum { + Foo { x: u32 } = 0, + Bar = 1, + Baz(String) = 2, + Quux = 3, +} +"#, + ); + } + + #[test] + fn non_primitive_repr_data_bearing_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo, + Bar(u16), + Baz, +} +"#, + ); + } + + #[test] + fn primitive_repr_non_data_bearing_add_discriminant() { + check_assist( + explicit_enum_discriminant, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar, + Baz$0 = 0x7fff_ffff_ffff_fffe, + Quux, +} +"#, + r#" +#[repr(i64)] +enum TheEnum { + Foo = 1 << 63, + Bar = -9223372036854775807, + Baz = 0x7fff_ffff_ffff_fffe, + Quux = 9223372036854775807, +} +"#, + ); + } + + #[test] + fn discriminants_already_explicit_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 { + Foo = 0, + Bar = 4, +} +"#, + ); + } + + #[test] + fn empty_enum_not_applicable() { + check_assist_not_applicable( + explicit_enum_discriminant, + r#" +enum TheEnum$0 {} +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs index dcf16e89b2c4..1eaf31628f31 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -2,7 +2,7 @@ use either::Either; use ide_db::syntax_helpers::node_ext::walk_ty; use syntax::{ ast::{self, edit::IndentLevel, make, AstNode, HasGenericArgs, HasGenericParams, HasName}, - ted, + syntax_editor, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -43,9 +43,8 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> AssistId("extract_type_alias", AssistKind::RefactorExtract), "Extract type as type alias", target, - |edit| { - let node = edit.make_syntax_mut(node.clone()); - let target_ty = edit.make_mut(ty.clone()); + |builder| { + let mut edit = builder.make_editor(node); let mut known_generics = match item.generic_param_list() { Some(it) => it.generic_params().collect(), @@ -67,25 +66,28 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> .map_or(String::new(), |it| it.to_generic_args().to_string()); // FIXME: replace with a `ast::make` constructor let new_ty = make::ty(&format!("Type{ty_args}")).clone_for_update(); - ted::replace(target_ty.syntax(), new_ty.syntax()); + edit.replace(ty.syntax(), new_ty.syntax()); // Insert new alias - let indent = IndentLevel::from_node(&node); let ty_alias = make::ty_alias("Type", generic_params, None, None, Some((ty, None))) .clone_for_update(); - ted::insert_all( - ted::Position::before(node), + + if let Some(cap) = ctx.config.snippet_cap { + if let Some(name) = ty_alias.name() { + edit.add_annotation(name.syntax(), builder.make_tabstop_before(cap)); + } + } + + let indent = IndentLevel::from_node(node); + edit.insert_all( + syntax_editor::Position::before(node), vec![ ty_alias.syntax().clone().into(), make::tokens::whitespace(&format!("\n\n{indent}")).into(), ], ); - if let Some(cap) = ctx.config.snippet_cap { - if let Some(name) = ty_alias.name() { - edit.add_tabstop_before(cap, name); - } - } + builder.add_file_edits(ctx.file_id(), edit); }, ) } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs index 5ae75bb1ff8e..a43a4b5e1a06 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs @@ -1,4 +1,5 @@ use hir::TypeInfo; +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName}, ted, NodeOrToken, @@ -6,7 +7,7 @@ use syntax::{ SyntaxNode, T, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_variable // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs index f40f2713ad13..af2c2c759ec7 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/flip_comma.rs @@ -1,4 +1,8 @@ -use syntax::{algo::non_trivia_sibling, Direction, SyntaxKind, T}; +use ide_db::base_db::SourceDatabase; +use syntax::TextSize; +use syntax::{ + algo::non_trivia_sibling, ast, AstNode, Direction, SyntaxKind, SyntaxToken, TextRange, T, +}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -21,6 +25,8 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let comma = ctx.find_token_syntax_at_offset(T![,])?; let prev = non_trivia_sibling(comma.clone().into(), Direction::Prev)?; let next = non_trivia_sibling(comma.clone().into(), Direction::Next)?; + let (mut prev_text, mut next_text) = (prev.to_string(), next.to_string()); + let (mut prev_range, mut next_range) = (prev.text_range(), next.text_range()); // Don't apply a "flip" in case of a last comma // that typically comes before punctuation @@ -34,17 +40,55 @@ pub(crate) fn flip_comma(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( return None; } + if let Some(parent) = comma.parent().and_then(ast::TokenTree::cast) { + // An attribute. It often contains a path followed by a token tree (e.g. `align(2)`), so we have + // to be smarter. + let prev_start = + match comma.siblings_with_tokens(Direction::Prev).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_after_token(it.as_token().unwrap()), + None => position_after_token(&parent.left_delimiter_token()?), + }; + let prev_end = prev.text_range().end(); + let next_start = next.text_range().start(); + let next_end = + match comma.siblings_with_tokens(Direction::Next).skip(1).find(|it| it.kind() == T![,]) + { + Some(it) => position_before_token(it.as_token().unwrap()), + None => position_before_token(&parent.right_delimiter_token()?), + }; + prev_range = TextRange::new(prev_start, prev_end); + next_range = TextRange::new(next_start, next_end); + let file_text = ctx.db().file_text(ctx.file_id().file_id()); + prev_text = file_text[prev_range].to_owned(); + next_text = file_text[next_range].to_owned(); + } + acc.add( AssistId("flip_comma", AssistKind::RefactorRewrite), "Flip comma", comma.text_range(), |edit| { - edit.replace(prev.text_range(), next.to_string()); - edit.replace(next.text_range(), prev.to_string()); + edit.replace(prev_range, next_text); + edit.replace(next_range, prev_text); }, ) } +fn position_before_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Prev) { + Some(prev_token) => prev_token.text_range().end(), + None => token.text_range().start(), + } +} + +fn position_after_token(token: &SyntaxToken) -> TextSize { + match non_trivia_sibling(token.clone().into(), Direction::Next) { + Some(prev_token) => prev_token.text_range().start(), + None => token.text_range().end(), + } +} + #[cfg(test)] mod tests { use super::*; @@ -89,4 +133,18 @@ mod tests { // See https://github.com/rust-lang/rust-analyzer/issues/7693 check_assist_not_applicable(flip_comma, r#"bar!(a,$0 b)"#); } + + #[test] + fn flip_comma_attribute() { + check_assist( + flip_comma, + r#"#[repr(align(2),$0 C)] struct Foo;"#, + r#"#[repr(C, align(2))] struct Foo;"#, + ); + check_assist( + flip_comma, + r#"#[foo(bar, baz(1 + 1),$0 qux, other)] struct Foo;"#, + r#"#[foo(bar, qux, baz(1 + 1), other)] struct Foo;"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs index bf4ce5c907e8..66bf9b018686 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_delegate_trait.rs @@ -2,13 +2,14 @@ use std::ops::Not; use crate::{ assist_context::{AssistContext, Assists}, - utils::{convert_param_list_to_arg_list, suggest_name}, + utils::convert_param_list_to_arg_list, }; use either::Either; use hir::{db::HirDatabase, HasVisibility}; use ide_db::{ assists::{AssistId, GroupLabel}, path_transform::PathTransform, + syntax_helpers::suggest_name, FxHashMap, FxHashSet, }; use itertools::Itertools; @@ -281,8 +282,11 @@ fn generate_impl( ai.assoc_items() .filter(|item| matches!(item, AssocItem::MacroCall(_)).not()) .for_each(|item| { - let assoc = - process_assoc_item(item, qualified_path_type.clone(), field_name); + let assoc = process_assoc_item( + item.clone_for_update(), + qualified_path_type.clone(), + field_name, + ); if let Some(assoc) = assoc { delegate_assoc_items.add_item(assoc); } @@ -583,7 +587,7 @@ fn resolve_name_conflicts( for old_strukt_param in old_strukt_params.generic_params() { // Get old name from `strukt` - let mut name = SmolStr::from(match &old_strukt_param { + let name = SmolStr::from(match &old_strukt_param { ast::GenericParam::ConstParam(c) => c.name()?.to_string(), ast::GenericParam::LifetimeParam(l) => { l.lifetime()?.lifetime_ident_token()?.to_string() @@ -592,8 +596,19 @@ fn resolve_name_conflicts( }); // The new name cannot be conflicted with generics in trait, and the renamed names. - name = suggest_name::for_unique_generic_name(&name, old_impl_params); - name = suggest_name::for_unique_generic_name(&name, ¶ms); + let param_list_to_names = |param_list: &GenericParamList| { + param_list.generic_params().flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + }; + let existing_names = param_list_to_names(old_impl_params) + .chain(param_list_to_names(¶ms)) + .collect_vec(); + let mut name_generator = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ); + let name = name_generator.suggest_name(&name); match old_strukt_param { ast::GenericParam::ConstParam(c) => { if let Some(const_ty) = c.ty() { @@ -1212,9 +1227,9 @@ struct S { b : B, } -impl Trait for S { - fn f(&self, a: T0) -> T0 { - as Trait>::f(&self.b, a) +impl Trait for S { + fn f(&self, a: T1) -> T1 { + as Trait>::f(&self.b, a) } } "#, @@ -1526,12 +1541,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T10: AnotherTrait + T3: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); @@ -1588,12 +1603,12 @@ where b : B, } -impl Trait for S +impl Trait for S where - T0: AnotherTrait + T2: AnotherTrait { fn f(&self, a: T) -> T { - as Trait>::f(&self.b, a) + as Trait>::f(&self.b, a) } }"#, ); @@ -1785,4 +1800,40 @@ impl T for B { "#, ); } + + #[test] + fn assoc_items_attributes_mutably_cloned() { + check_assist( + generate_delegate_trait, + r#" +pub struct A; +pub trait C { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D; +} + +pub struct B> { + has_dr$0ain: T, +} +"#, + r#" +pub struct A; +pub trait C { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D; +} + +pub struct B> { + has_drain: T, +} + +impl> C for B { + #[allow(clippy::dead_code)] + fn a_funk(&self) -> &D { + >::a_funk(&self.has_drain) + } +} +"#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs index 821783c28313..7b7dac9a3d6c 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs @@ -1,10 +1,22 @@ use syntax::{ - ast::{self, make, AstNode, HasName}, + ast::{self, edit_in_place::Indent, make, AstNode, HasName}, ted, }; use crate::{utils, AssistContext, AssistId, AssistKind, Assists}; +fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) { + let indent = nominal.indent_level(); + ted::insert_all_raw( + ted::Position::after(nominal.syntax()), + vec![ + // Add a blank line after the ADT, and indentation for the impl to match the ADT + make::tokens::whitespace(&format!("\n\n{indent}")).into(), + impl_.syntax().clone().into(), + ], + ); +} + // Assist: generate_impl // // Adds a new inherent impl for a type. @@ -46,12 +58,7 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -97,12 +104,7 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> } } - // Add the impl after the adt - let nominal = edit.make_mut(nominal); - ted::insert_all_raw( - ted::Position::after(nominal.syntax()), - vec![make::tokens::blank_line().into(), impl_.syntax().clone().into()], - ); + insert_impl(impl_, &edit.make_mut(nominal)); }, ) } @@ -418,4 +420,65 @@ mod tests { "/// Has a lifetime parameter\nstruct Foo<'a, T: Foo<'a>> {}", ); } + + #[test] + fn add_impl_with_indent() { + check_assist( + generate_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl Bar {$0} + } + "#, + ); + } + + #[test] + fn add_impl_with_multiple_indent() { + check_assist( + generate_impl, + r#" + mod foo { + fn bar() { + struct Baz$0 {} + } + } + "#, + r#" + mod foo { + fn bar() { + struct Baz {} + + impl Baz {$0} + } + } + "#, + ); + } + + #[test] + fn add_trait_impl_with_indent() { + check_assist( + generate_trait_impl, + r#" + mod foo { + struct Bar$0 {} + } + "#, + r#" + mod foo { + struct Bar {} + + impl ${0:_} for Bar {} + } + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs index 5bd204dd573b..9e09f198feb4 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs @@ -2,14 +2,18 @@ use std::collections::BTreeSet; use ast::make; use either::Either; -use hir::{db::HirDatabase, sym, FileRange, PathResolution, Semantics, TypeInfo}; +use hir::{ + db::{ExpandDatabase, HirDatabase}, + sym, FileRange, PathResolution, Semantics, TypeInfo, +}; use ide_db::{ + base_db::CrateId, defs::Definition, imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, search::{FileReference, FileReferenceNode, SearchScope}, source_change::SourceChangeBuilder, - syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, + syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion}, EditionedFileId, RootDatabase, }; use itertools::{izip, Itertools}; @@ -102,12 +106,13 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut remove_def = true; let mut inline_refs_for_file = |file_id, refs: Vec| { builder.edit_file(file_id); + let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate()); let count = refs.len(); // The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️ let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some); let call_infos: Vec<_> = name_refs .into_iter() - .filter_map(CallInfo::from_name_ref) + .filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into())) // FIXME: do not handle callsites in macros' parameters, because // directly inlining into macros may cause errors. .filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro()) @@ -185,7 +190,10 @@ pub(super) fn split_refs_and_uses( // ``` pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let name_ref: ast::NameRef = ctx.find_node_at_offset()?; - let call_info = CallInfo::from_name_ref(name_ref.clone())?; + let call_info = CallInfo::from_name_ref( + name_ref.clone(), + ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(), + )?; let (function, label) = match &call_info.node { ast::CallableExpr::Call(call) => { let path = match call.expr()? { @@ -243,10 +251,11 @@ struct CallInfo { node: ast::CallableExpr, arguments: Vec, generic_arg_list: Option, + krate: CrateId, } impl CallInfo { - fn from_name_ref(name_ref: ast::NameRef) -> Option { + fn from_name_ref(name_ref: ast::NameRef, krate: CrateId) -> Option { let parent = name_ref.syntax().parent()?; if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) { let receiver = call.receiver()?; @@ -256,6 +265,7 @@ impl CallInfo { generic_arg_list: call.generic_arg_list(), node: ast::CallableExpr::MethodCall(call), arguments, + krate, }) } else if let Some(segment) = ast::PathSegment::cast(parent) { let path = segment.syntax().parent().and_then(ast::Path::cast)?; @@ -266,6 +276,7 @@ impl CallInfo { arguments: call.arg_list()?.args().collect(), node: ast::CallableExpr::Call(call), generic_arg_list: segment.generic_arg_list(), + krate, }) } else { None @@ -307,11 +318,15 @@ fn inline( function: hir::Function, fn_body: &ast::BlockExpr, params: &[(ast::Pat, Option, hir::Param)], - CallInfo { node, arguments, generic_arg_list }: &CallInfo, + CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo, ) -> ast::Expr { - let mut body = if sema.hir_file_for(fn_body.syntax()).is_macro() { + let file_id = sema.hir_file_for(fn_body.syntax()); + let mut body = if let Some(macro_file) = file_id.macro_file() { cov_mark::hit!(inline_call_defined_in_macro); - if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { + let span_map = sema.db.expansion_span_map(macro_file); + let body_prettified = + prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate); + if let Some(body) = ast::BlockExpr::cast(body_prettified) { body } else { fn_body.clone_for_update() @@ -420,8 +435,16 @@ fn inline( let mut insert_let_stmt = || { let param_ty = param_ty.clone().map(|param_ty| { - if sema.hir_file_for(param_ty.syntax()).is_macro() { - ast::Type::cast(insert_ws_into(param_ty.syntax().clone())).unwrap_or(param_ty) + let file_id = sema.hir_file_for(param_ty.syntax()); + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let param_ty_prettified = prettify_macro_expansion( + sema.db, + param_ty.syntax().clone(), + &span_map, + *krate, + ); + ast::Type::cast(param_ty_prettified).unwrap_or(param_ty) } else { param_ty } @@ -1020,6 +1043,7 @@ fn main() { check_assist( inline_call, r#" +//- minicore: sized fn foo(x: *const u32) -> u32 { x as u32 } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs index f1c2acdd3ed9..6b504a918b40 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_const_as_literal.rs @@ -53,10 +53,7 @@ pub(crate) fn inline_const_as_literal(acc: &mut Assists, ctx: &AssistContext<'_> | ast::Expr::BinExpr(_) | ast::Expr::CallExpr(_) => { let edition = ctx.sema.scope(variable.syntax())?.krate().edition(ctx.db()); - match konst.render_eval(ctx.sema.db, edition) { - Ok(result) => result, - Err(_) => return None, - } + konst.eval(ctx.sema.db, edition).ok()? } _ => return None, }; @@ -127,12 +124,14 @@ mod tests { ("u64", "0", NUMBER), ("u128", "0", NUMBER), ("usize", "0", NUMBER), + ("usize", "16", NUMBER), ("i8", "0", NUMBER), ("i16", "0", NUMBER), ("i32", "0", NUMBER), ("i64", "0", NUMBER), ("i128", "0", NUMBER), ("isize", "0", NUMBER), + ("isize", "16", NUMBER), ("bool", "false", BOOL), ("&str", "\"str\"", STR), ("char", "'c'", CHAR), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs index 6a1f7f26c92c..b9fc075ae838 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -333,7 +333,8 @@ fn foo() { check_assist( inline_local_variable, r" -fn bar(a: usize): usize { a } +//- minicore: sized +fn bar(a: usize) -> usize { a } fn foo() { let a$0 = bar(1) as u64; a + 1; @@ -347,7 +348,7 @@ fn foo() { bar(a); }", r" -fn bar(a: usize): usize { a } +fn bar(a: usize) -> usize { a } fn foo() { (bar(1) as u64) + 1; if (bar(1) as u64) > 10 { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs index 4708be616964..d558ec3bec7d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs @@ -1,4 +1,5 @@ -use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into; +use hir::db::ExpandDatabase; +use ide_db::syntax_helpers::prettify_macro_expansion; use syntax::ast::{self, AstNode}; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -36,7 +37,15 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // ``` pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let unexpanded = ctx.find_node_at_offset::()?; - let expanded = insert_ws_into(ctx.sema.expand(&unexpanded)?.clone_for_update()); + let macro_call = ctx.sema.to_def(&unexpanded)?; + let expanded = ctx.sema.parse_or_expand(macro_call.as_file()); + let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file()); + let expanded = prettify_macro_expansion( + ctx.db(), + expanded, + &span_map, + ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(), + ); let text_range = unexpanded.syntax().text_range(); acc.add( @@ -295,6 +304,75 @@ fn main() { } }; } +"#, + ); + } + + #[test] + fn dollar_crate() { + check_assist( + inline_macro, + r#" +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +fn bar() { + m$0!(); +} +"#, + r#" +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +fn bar() { + crate::Foo; +} +"#, + ); + check_assist( + inline_macro, + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +//- /b.rs crate:b deps:a +fn bar() { + a::m$0!(); +} +"#, + r#" +fn bar() { + a::Foo; +} +"#, + ); + check_assist( + inline_macro, + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { $crate::Foo }; +} +//- /b.rs crate:b deps:a +pub use a::m; +//- /c.rs crate:c deps:b +fn bar() { + b::m$0!(); +} +"#, + r#" +fn bar() { + a::Foo; +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index f6624d6c872d..66dffde505c1 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -43,6 +43,7 @@ use super::inline_call::split_refs_and_uses; // fn foo() { // let _: i32 = 3; // } +// ``` pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let name = ctx.find_node_at_offset::()?; let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?; diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs index 543b7f7ab632..bf6ac1719f31 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/introduce_named_generic.rs @@ -1,9 +1,11 @@ +use ide_db::syntax_helpers::suggest_name; +use itertools::Itertools; use syntax::{ - ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams}, + ast::{self, edit_in_place::GenericParamsOwnerEdit, make, AstNode, HasGenericParams, HasName}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: introduce_named_generic // @@ -32,8 +34,18 @@ pub(crate) fn introduce_named_generic(acc: &mut Assists, ctx: &AssistContext<'_> let impl_trait_type = edit.make_mut(impl_trait_type); let fn_ = edit.make_mut(fn_); let fn_generic_param_list = fn_.get_or_create_generic_param_list(); - let type_param_name = - suggest_name::for_impl_trait_as_generic(&impl_trait_type, &fn_generic_param_list); + + let existing_names = fn_generic_param_list + .generic_params() + .flat_map(|param| match param { + ast::GenericParam::TypeParam(t) => t.name().map(|name| name.to_string()), + p => Some(p.to_string()), + }) + .collect_vec(); + let type_param_name = suggest_name::NameGenerator::new_with_names( + existing_names.iter().map(|s| s.as_str()), + ) + .for_impl_trait_as_generic(&impl_trait_type); let type_param = make::type_param(make::name(&type_param_name), Some(type_bound_list)) .clone_for_update(); @@ -115,7 +127,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B0) {}"#, + r#"fn foo(bar: B1) {}"#, ); } @@ -124,7 +136,7 @@ fn foo<$0B: Bar check_assist( introduce_named_generic, r#"fn foo(bar: $0impl Bar) {}"#, - r#"fn foo(bar: B2) {}"#, + r#"fn foo(bar: B4) {}"#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs index d4fdc072fb5b..c6f99d68748d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_unused_imports.rs @@ -6,7 +6,10 @@ use ide_db::{ search::{FileReference, ReferenceCategory, SearchScope}, FxHashMap, RootDatabase, }; -use syntax::{ast, AstNode}; +use syntax::{ + ast::{self, Rename}, + AstNode, +}; use text_edit::TextRange; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -100,19 +103,19 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) hir::ScopeDef::ModuleDef(d) => Some(Definition::from(*d)), _ => None, }) - .any(|d| used_once_in_scope(ctx, d, scope)) + .any(|d| used_once_in_scope(ctx, d, u.rename(), scope)) { return Some(u); } } else if let Definition::Trait(ref t) = def { // If the trait or any item is used. - if !std::iter::once(def) - .chain(t.items(ctx.db()).into_iter().map(Definition::from)) - .any(|d| used_once_in_scope(ctx, d, scope)) + if !std::iter::once((def, u.rename())) + .chain(t.items(ctx.db()).into_iter().map(|item| (item.into(), None))) + .any(|(d, rename)| used_once_in_scope(ctx, d, rename, scope)) { return Some(u); } - } else if !used_once_in_scope(ctx, def, scope) { + } else if !used_once_in_scope(ctx, def, u.rename(), scope) { return Some(u); } @@ -138,7 +141,12 @@ pub(crate) fn remove_unused_imports(acc: &mut Assists, ctx: &AssistContext<'_>) } } -fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec) -> bool { +fn used_once_in_scope( + ctx: &AssistContext<'_>, + def: Definition, + rename: Option, + scopes: &Vec, +) -> bool { let mut found = false; for scope in scopes { @@ -151,7 +159,10 @@ fn used_once_in_scope(ctx: &AssistContext<'_>, def: Definition, scopes: &Vec Quxx for T {} +} + +use foo::{Foo as Bar, Bar as Baz, Qux as _, Quxx as _}$0; + +fn test(_: Bar) { + let a = (); + a.quxx(); +} +"#, + r#" +mod foo { + pub struct Foo {} + pub struct Bar {} + pub struct Qux {} + pub trait Quux { + fn quxx(&self) {} + } + impl Quxx for T {} +} + +use foo::{Foo as Bar, Quxx as _}; + +fn test(_: Bar) { + let a = (); + a.quxx(); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 59bb0c45e143..a856da092153 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs @@ -1,9 +1,10 @@ +use ide_db::syntax_helpers::suggest_name; use syntax::{ ast::{self, make, AstNode}, ted, }; -use crate::{utils::suggest_name, AssistContext, AssistId, AssistKind, Assists}; +use crate::{AssistContext, AssistId, AssistKind, Assists}; // Assist: replace_is_some_with_if_let_some // diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 65330b34c4ad..1101c20f1b78 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs @@ -29,7 +29,7 @@ pub(crate) fn replace_qualified_name_with_use( acc: &mut Assists, ctx: &AssistContext<'_>, ) -> Option<()> { - let original_path: ast::Path = ctx.find_node_at_offset()?; + let mut original_path: ast::Path = ctx.find_node_at_offset()?; // We don't want to mess with use statements if original_path.syntax().ancestors().find_map(ast::UseTree::cast).is_some() { cov_mark::hit!(not_applicable_in_use); @@ -37,8 +37,7 @@ pub(crate) fn replace_qualified_name_with_use( } if original_path.qualifier().is_none() { - cov_mark::hit!(dont_import_trivial_paths); - return None; + original_path = original_path.parent_path()?; } // only offer replacement for non assoc items @@ -236,12 +235,6 @@ fs::Path ); } - #[test] - fn dont_import_trivial_paths() { - cov_mark::check!(dont_import_trivial_paths); - check_assist_not_applicable(replace_qualified_name_with_use, r"impl foo$0 for () {}"); - } - #[test] fn test_replace_not_applicable_in_use() { cov_mark::check!(not_applicable_in_use); @@ -271,6 +264,29 @@ fn main() { ); } + #[test] + fn assist_runs_on_first_segment() { + check_assist( + replace_qualified_name_with_use, + r" +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + $0std::fmt::Debug; + let x: std::fmt::Debug = std::fmt::Debug; +} + ", + r" +use std::fmt; + +mod std { pub mod fmt { pub trait Debug {} } } +fn main() { + fmt::Debug; + let x: fmt::Debug = fmt::Debug; +} + ", + ); + } + #[test] fn does_not_replace_in_submodules() { check_assist( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index eedb2ea3b9ad..e452b5f77870 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs @@ -64,12 +64,9 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>) acc.add( AssistId("toggle_macro_delimiter", AssistKind::Refactor), match token { - MacroDelims::LPar => "Replace delimiters with braces", - MacroDelims::RPar => "Replace delimiters with braces", - MacroDelims::LBra => "Replace delimiters with parentheses", - MacroDelims::RBra => "Replace delimiters with parentheses", - MacroDelims::LCur => "Replace delimiters with brackets", - MacroDelims::RCur => "Replace delimiters with brackets", + MacroDelims::LPar | MacroDelims::RPar => "Replace delimiters with braces", + MacroDelims::LBra | MacroDelims::RBra => "Replace delimiters with parentheses", + MacroDelims::LCur | MacroDelims::RCur => "Replace delimiters with brackets", }, token_tree.syntax().text_range(), |builder| { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b68ed00f7721..8f0e9b4fe09d 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs @@ -1,12 +1,14 @@ use std::iter; +use hir::HasSource; use ide_db::{ famous_defs::FamousDefs, syntax_helpers::node_ext::{for_each_tail_expr, walk_expr}, }; +use itertools::Itertools; use syntax::{ - ast::{self, make, Expr}, - match_ast, ted, AstNode, + ast::{self, make, Expr, HasGenericParams}, + match_ast, ted, AstNode, ToSmolStr, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -39,25 +41,22 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< }; let type_ref = &ret_type.ty()?; - let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); - let result_enum = + let core_result = FamousDefs(&ctx.sema, ctx.sema.scope(type_ref.syntax())?.krate()).core_result_Result()?; - if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == result_enum) { + let ty = ctx.sema.resolve_type(type_ref)?.as_adt(); + if matches!(ty, Some(hir::Adt::Enum(ret_type)) if ret_type == core_result) { + // The return type is already wrapped in a Result cov_mark::hit!(wrap_return_type_in_result_simple_return_type_already_result); return None; } - let new_result_ty = - make::ext::ty_result(type_ref.clone(), make::ty_placeholder()).clone_for_update(); - let generic_args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast)?; - let last_genarg = generic_args.generic_args().last()?; - acc.add( AssistId("wrap_return_type_in_result", AssistKind::RefactorRewrite), "Wrap return type in Result", type_ref.syntax().text_range(), |edit| { + let new_result_ty = result_type(ctx, &core_result, type_ref).clone_for_update(); let body = edit.make_mut(ast::Expr::BlockExpr(body)); let mut exprs_to_wrap = Vec::new(); @@ -81,16 +80,72 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< } let old_result_ty = edit.make_mut(type_ref.clone()); - ted::replace(old_result_ty.syntax(), new_result_ty.syntax()); - if let Some(cap) = ctx.config.snippet_cap { - edit.add_placeholder_snippet(cap, last_genarg); + // Add a placeholder snippet at the first generic argument that doesn't equal the return type. + // This is normally the error type, but that may not be the case when we inserted a type alias. + let args = new_result_ty.syntax().descendants().find_map(ast::GenericArgList::cast); + let error_type_arg = args.and_then(|list| { + list.generic_args().find(|arg| match arg { + ast::GenericArg::TypeArg(_) => arg.syntax().text() != type_ref.syntax().text(), + ast::GenericArg::LifetimeArg(_) => false, + _ => true, + }) + }); + if let Some(error_type_arg) = error_type_arg { + if let Some(cap) = ctx.config.snippet_cap { + edit.add_placeholder_snippet(cap, error_type_arg); + } } }, ) } +fn result_type( + ctx: &AssistContext<'_>, + core_result: &hir::Enum, + ret_type: &ast::Type, +) -> ast::Type { + // Try to find a Result type alias in the current scope (shadowing the default). + let result_path = hir::ModPath::from_segments( + hir::PathKind::Plain, + iter::once(hir::Name::new_symbol_root(hir::sym::Result.clone())), + ); + let alias = ctx.sema.resolve_mod_path(ret_type.syntax(), &result_path).and_then(|def| { + def.filter_map(|def| match def.as_module_def()? { + hir::ModuleDef::TypeAlias(alias) => { + let enum_ty = alias.ty(ctx.db()).as_adt()?.as_enum()?; + (&enum_ty == core_result).then_some(alias) + } + _ => None, + }) + .find_map(|alias| { + let mut inserted_ret_type = false; + let generic_params = alias + .source(ctx.db())? + .value + .generic_param_list()? + .generic_params() + .map(|param| match param { + // Replace the very first type parameter with the functions return type. + ast::GenericParam::TypeParam(_) if !inserted_ret_type => { + inserted_ret_type = true; + ret_type.to_smolstr() + } + ast::GenericParam::LifetimeParam(_) => make::lifetime("'_").to_smolstr(), + _ => make::ty_placeholder().to_smolstr(), + }) + .join(", "); + + let name = alias.name(ctx.db()); + let name = name.as_str(); + Some(make::ty(&format!("{name}<{generic_params}>"))) + }) + }); + // If there is no applicable alias in scope use the default Result type. + alias.unwrap_or_else(|| make::ext::ty_result(ret_type.clone(), make::ty_placeholder())) +} + fn tail_cb_impl(acc: &mut Vec, e: &ast::Expr) { match e { Expr::BreakExpr(break_expr) => { @@ -998,4 +1053,216 @@ fn foo(the_field: u32) -> Result { "#, ); } + + #[test] + fn wrap_return_type_in_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result2 = core::result::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +type Result2 = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_imported_local_result_type() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> i3$02 { + return 42i32; +} +"#, + r#" +mod some_module { + pub type Result = core::result::Result; +} + +use some_module::*; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_from_function_body() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +fn foo() -> i3$02 { + type Result = core::result::Result; + 0 +} +"#, + r#" +fn foo() -> Result { + type Result = core::result::Result; + Ok(0) +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_already_using_alias() { + check_assist_not_applicable( + wrap_return_type_in_result, + r#" +//- minicore: result +pub type Result = core::result::Result; + +fn foo() -> Result { + return Ok(42i32); +} +"#, + ); + } + + #[test] + fn wrap_return_type_in_local_result_type_multiple_generics() { + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result; + +fn foo() -> i3$02 { + 0 +} +"#, + r#" +type Result = core::result::Result; + +fn foo() -> Result { + Ok(0) +} +"#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, ()>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result<'a, T, E> = core::result::Result, &'a ()>; + +fn foo() -> Result<'_, i32, ${0:_}> { + Ok(0) +} + "#, + ); + + check_assist( + wrap_return_type_in_result, + r#" +//- minicore: result +type Result = core::result::Result, Bar>; + +fn foo() -> i3$02 { + 0 +} + "#, + r#" +type Result = core::result::Result, Bar>; + +fn foo() -> Result { + Ok(0) +} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 0fa46ef43a1e..149cb4c43849 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs @@ -25,6 +25,7 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // struct S { // field: i32 // } +// ``` enum WrapUnwrapOption { WrapDerive { derive: TextRange, attr: ast::Attr }, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs index c88cb3d5eaf0..c98655b42302 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs @@ -58,6 +58,8 @@ //! See also this post: //! +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + mod assist_config; mod assist_context; #[cfg(test)] @@ -136,6 +138,7 @@ mod handlers { mod destructure_tuple_binding; mod desugar_doc_comment; mod expand_glob_import; + mod explicit_enum_discriminant; mod extract_expressions_from_format_string; mod extract_function; mod extract_module; @@ -266,6 +269,7 @@ mod handlers { destructure_tuple_binding::destructure_tuple_binding, destructure_struct_binding::destructure_struct_binding, expand_glob_import::expand_glob_import, + explicit_enum_discriminant::explicit_enum_discriminant, extract_expressions_from_format_string::extract_expressions_from_format_string, extract_struct_from_enum_variant::extract_struct_from_enum_variant, extract_type_alias::extract_type_alias, diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs index dce7bbf342f7..48e12a810735 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs @@ -909,6 +909,29 @@ fn qux(bar: Bar, baz: Baz) {} ) } +#[test] +fn doctest_explicit_enum_discriminant() { + check_doc_test( + "explicit_enum_discriminant", + r#####" +enum TheEnum$0 { + Foo, + Bar, + Baz = 42, + Quux, +} +"#####, + r#####" +enum TheEnum { + Foo = 0, + Bar = 1, + Baz = 42, + Quux = 43, +} +"#####, + ) +} + #[test] fn doctest_extract_expressions_from_format_string() { check_doc_test( diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs index b8a6f3b6dbeb..0830017bd0fd 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs @@ -1,10 +1,13 @@ //! Assorted functions shared by several assists. pub(crate) use gen_trait_fn_body::gen_trait_fn_body; -use hir::{db::HirDatabase, HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics}; +use hir::{ + db::{ExpandDatabase, HirDatabase}, + HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics, +}; use ide_db::{ famous_defs::FamousDefs, path_transform::PathTransform, - syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase, + syntax_helpers::prettify_macro_expansion, RootDatabase, }; use stdx::format_to; use syntax::{ @@ -23,7 +26,6 @@ use crate::assist_context::{AssistContext, SourceChangeBuilder}; mod gen_trait_fn_body; pub(crate) mod ref_field_expr; -pub(crate) mod suggest_name; pub(crate) fn unwrap_trivial_block(block_expr: ast::BlockExpr) -> ast::Expr { extract_trivial_expression(&block_expr) @@ -179,10 +181,15 @@ pub fn add_trait_assoc_items_to_impl( let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1; let items = original_items.iter().map(|InFile { file_id, value: original_item }| { let cloned_item = { - if file_id.is_macro() { - if let Some(formatted) = - ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone())) - { + if let Some(macro_file) = file_id.macro_file() { + let span_map = sema.db.expansion_span_map(macro_file); + let item_prettified = prettify_macro_expansion( + sema.db, + original_item.syntax().clone(), + &span_map, + target_scope.krate().into(), + ); + if let Some(formatted) = ast::AssocItem::cast(item_prettified) { return formatted; } else { stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`"); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs index b537150608bb..414627fbabae 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions.rs @@ -617,6 +617,16 @@ impl Completions { } self.add_opt(render_struct_pat(RenderContext::new(ctx), pattern_ctx, strukt, local_name)); } + + pub(crate) fn suggest_name(&mut self, ctx: &CompletionContext<'_>, name: &str) { + let item = CompletionItem::new( + CompletionItemKind::Binding, + ctx.source_range(), + SmolStr::from(name), + ctx.edition, + ); + item.add_to(self, ctx.db); + } } /// Calls the callback for each variant of the provided enum with the path to the variant. diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs index 9821fb4a2fa9..d0b489c4e836 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/attribute.rs @@ -56,7 +56,7 @@ pub(crate) fn complete_known_attribute_input( &parse_tt_as_comma_sep_paths(tt, ctx.edition)?, FEATURES, ), - "allow" | "warn" | "deny" | "forbid" => { + "allow" | "expect" | "deny" | "forbid" | "warn" => { let existing_lints = parse_tt_as_comma_sep_paths(tt, ctx.edition)?; let lints: Vec = CLIPPY_LINT_GROUPS @@ -222,7 +222,7 @@ macro_rules! attrs { [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ }; // starting matcher [$($tt:tt),*] => { - attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" }) + attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "expect", "forbid", "warn" }) }; } @@ -303,6 +303,7 @@ const ATTRIBUTES: &[AttrCompletion] = &[ attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)), attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)), attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)), + attr("expect(…)", Some("expect"), Some("expect(${0:lint})")), attr( r#"export_name = "…""#, Some("export_name"), diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs index d55bc3ea5d03..f2c360a9d5bf 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/dot.rs @@ -600,7 +600,7 @@ fn foo(a: A) { a.$0 } struct A {} trait Trait { fn the_method(&self); } impl Trait for A {} -fn foo(a: A) { a.the_method()$0 } +fn foo(a: A) { a.the_method();$0 } "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs index fc6e1ebf05fc..672e1796d1ef 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -31,14 +31,14 @@ //! } //! ``` -use hir::HasAttrs; +use hir::{db::ExpandDatabase, HasAttrs, MacroFileId, Name}; use ide_db::{ documentation::HasDocs, path_transform::PathTransform, - syntax_helpers::insert_whitespace_into_node, traits::get_missing_assoc_items, SymbolKind, + syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items, SymbolKind, }; use syntax::{ - ast::{self, edit_in_place::AttrsOwnerEdit, HasTypeBounds}, - format_smolstr, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, + ast::{self, edit_in_place::AttrsOwnerEdit, make, HasGenericArgs, HasTypeBounds}, + format_smolstr, ted, AstNode, SmolStr, SyntaxElement, SyntaxKind, TextRange, ToSmolStr, T, }; use text_edit::TextEdit; @@ -178,12 +178,36 @@ fn add_function_impl( func: hir::Function, impl_def: hir::Impl, ) { - let fn_name = func.name(ctx.db); + let fn_name = &func.name(ctx.db); + let sugar: &[_] = if func.is_async(ctx.db) { + &[AsyncSugaring::Async, AsyncSugaring::Desugar] + } else if func.returns_impl_future(ctx.db) { + &[AsyncSugaring::Plain, AsyncSugaring::Resugar] + } else { + &[AsyncSugaring::Plain] + }; + for &sugaring in sugar { + add_function_impl_(acc, ctx, replacement_range, func, impl_def, fn_name, sugaring); + } +} - let is_async = func.is_async(ctx.db); +fn add_function_impl_( + acc: &mut Completions, + ctx: &CompletionContext<'_>, + replacement_range: TextRange, + func: hir::Function, + impl_def: hir::Impl, + fn_name: &Name, + async_sugaring: AsyncSugaring, +) { + let async_ = if let AsyncSugaring::Async | AsyncSugaring::Resugar = async_sugaring { + "async " + } else { + "" + }; let label = format_smolstr!( "{}fn {}({})", - if is_async { "async " } else { "" }, + async_, fn_name.display(ctx.db, ctx.edition), if func.assoc_fn_params(ctx.db).is_empty() { "" } else { ".." } ); @@ -195,23 +219,16 @@ fn add_function_impl( }); let mut item = CompletionItem::new(completion_kind, replacement_range, label, ctx.edition); - item.lookup_by(format!( - "{}fn {}", - if is_async { "async " } else { "" }, - fn_name.display(ctx.db, ctx.edition) - )) - .set_documentation(func.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + item.lookup_by(format!("{}fn {}", async_, fn_name.display(ctx.db, ctx.edition))) + .set_documentation(func.docs(ctx.db)) + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(func) { - let assoc_item = ast::AssocItem::Fn(source.value); - if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { - let transformed_fn = match transformed_item { - ast::AssocItem::Fn(func) => func, - _ => unreachable!(), - }; - - let function_decl = function_declaration(&transformed_fn, source.file_id.is_macro()); + if let Some(transformed_fn) = + get_transformed_fn(ctx, source.value, impl_def, async_sugaring) + { + let function_decl = + function_declaration(ctx, &transformed_fn, source.file_id.macro_file()); match ctx.config.snippet_cap { Some(cap) => { let snippet = format!("{function_decl} {{\n $0\n}}"); @@ -227,6 +244,14 @@ fn add_function_impl( } } +#[derive(Copy, Clone)] +enum AsyncSugaring { + Desugar, + Resugar, + Async, + Plain, +} + /// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. fn get_transformed_assoc_item( ctx: &CompletionContext<'_>, @@ -251,6 +276,82 @@ fn get_transformed_assoc_item( Some(assoc_item) } +/// Transform a relevant associated item to inline generics from the impl, remove attrs and docs, etc. +fn get_transformed_fn( + ctx: &CompletionContext<'_>, + fn_: ast::Fn, + impl_def: hir::Impl, + async_: AsyncSugaring, +) -> Option { + let trait_ = impl_def.trait_(ctx.db)?; + let source_scope = &ctx.sema.scope(fn_.syntax())?; + let target_scope = &ctx.sema.scope(ctx.sema.source(impl_def)?.syntax().value)?; + let transform = PathTransform::trait_impl( + target_scope, + source_scope, + trait_, + ctx.sema.source(impl_def)?.value, + ); + + let fn_ = fn_.clone_for_update(); + // FIXME: Paths in nested macros are not handled well. See + // `macro_generated_assoc_item2` test. + transform.apply(fn_.syntax()); + fn_.remove_attrs_and_docs(); + match async_ { + AsyncSugaring::Desugar => { + match fn_.ret_type() { + Some(ret_ty) => { + let ty = ret_ty.ty()?; + ted::replace( + ty.syntax(), + make::ty(&format!("impl Future")) + .syntax() + .clone_for_update(), + ); + } + None => ted::append_child( + fn_.param_list()?.syntax(), + make::ret_type(make::ty("impl Future")) + .syntax() + .clone_for_update(), + ), + } + fn_.async_token().unwrap().detach(); + } + AsyncSugaring::Resugar => { + let ty = fn_.ret_type()?.ty()?; + match &ty { + // best effort guessing here + ast::Type::ImplTraitType(t) => { + let output = t.type_bound_list()?.bounds().find_map(|b| match b.ty()? { + ast::Type::PathType(p) => { + let p = p.path()?.segment()?; + if p.name_ref()?.text() != "Future" { + return None; + } + match p.generic_arg_list()?.generic_args().next()? { + ast::GenericArg::AssocTypeArg(a) + if a.name_ref()?.text() == "Output" => + { + a.ty() + } + _ => None, + } + } + _ => None, + })?; + ted::replace(ty.syntax(), output.syntax()); + } + _ => (), + } + ted::prepend_child(fn_.syntax(), make::token(T![async])); + } + AsyncSugaring::Async | AsyncSugaring::Plain => (), + } + Some(fn_) +} + fn add_type_alias_impl( acc: &mut Completions, ctx: &CompletionContext<'_>, @@ -266,7 +367,7 @@ fn add_type_alias_impl( CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label, ctx.edition); item.lookup_by(format!("type {alias_name}")) .set_documentation(type_alias.docs(ctx.db)) - .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); + .set_relevance(CompletionRelevance { exact_name_match: true, ..Default::default() }); if let Some(source) = ctx.sema.source(type_alias) { let assoc_item = ast::AssocItem::TypeAlias(source.value); @@ -332,7 +433,8 @@ fn add_const_impl( _ => unreachable!(), }; - let label = make_const_compl_syntax(&transformed_const, source.file_id.is_macro()); + let label = + make_const_compl_syntax(ctx, &transformed_const, source.file_id.macro_file()); let replacement = format!("{label} "); let mut item = @@ -340,7 +442,7 @@ fn add_const_impl( item.lookup_by(format_smolstr!("const {const_name}")) .set_documentation(const_.docs(ctx.db)) .set_relevance(CompletionRelevance { - is_item_from_trait: true, + exact_name_match: true, ..Default::default() }); match ctx.config.snippet_cap { @@ -356,9 +458,14 @@ fn add_const_impl( } } -fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> SmolStr { - let const_ = if needs_whitespace { - insert_whitespace_into_node::insert_ws_into(const_.syntax().clone()) +fn make_const_compl_syntax( + ctx: &CompletionContext<'_>, + const_: &ast::Const, + macro_file: Option, +) -> SmolStr { + let const_ = if let Some(macro_file) = macro_file { + let span_map = ctx.db.expansion_span_map(macro_file); + prettify_macro_expansion(ctx.db, const_.syntax().clone(), &span_map, ctx.krate.into()) } else { const_.syntax().clone() }; @@ -379,9 +486,14 @@ fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> SmolS format_smolstr!("{} =", syntax.trim_end()) } -fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { - let node = if needs_whitespace { - insert_whitespace_into_node::insert_ws_into(node.syntax().clone()) +fn function_declaration( + ctx: &CompletionContext<'_>, + node: &ast::Fn, + macro_file: Option, +) -> String { + let node = if let Some(macro_file) = macro_file { + let span_map = ctx.db.expansion_span_map(macro_file); + prettify_macro_expansion(ctx.db, node.syntax().clone(), &span_map, ctx.krate.into()) } else { node.syntax().clone() }; @@ -1401,6 +1513,134 @@ trait Tr { impl Tr for () { type Item = $0; } +"#, + ); + } + + #[test] + fn impl_fut() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future + Send { + $0 +} +} +"#, + ); + } + + #[test] + fn impl_fut_resugared() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + fn foo(&self) -> impl Future + Send; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} +"#, + ); + } + + #[test] + fn async_desugared() { + check_edit( + "fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + fn foo(&self) -> impl Future { + $0 +} +} +"#, + ); + } + + #[test] + fn async_() { + check_edit( + "async fn foo", + r#" +//- minicore: future, send, sized +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + $0 +} +"#, + r#" +use core::future::Future; + +trait DesugaredAsyncTrait { + async fn foo(&self) -> usize; +} + +impl DesugaredAsyncTrait for () { + async fn foo(&self) -> usize { + $0 +} +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs index 3f50cd55cb36..0acb87872f5e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/keyword.rs @@ -150,6 +150,68 @@ fn foo(a: A) { a.$0 } ); } + #[test] + fn for_in_impl() { + check_edit( + "for", + r#" +struct X; +impl X $0 {} +"#, + r#" +struct X; +impl X for $0 {} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 {} +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 {} +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X $0 +} +"#, + r#" +fn foo() { + struct X; + impl X for $0 +} +"#, + ); + check_edit( + "for", + r#" +fn foo() { + struct X; + impl X { fn bar() { $0 } } +} +"#, + r#" +fn foo() { + struct X; + impl X { fn bar() { for $1 in $2 { + $0 +} } } +} +"#, + ); + } + #[test] fn let_semi() { cov_mark::check!(let_semi); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs index 60cfb7e5a8c5..8f38e02ed768 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/pattern.rs @@ -1,6 +1,7 @@ //! Completes constants and paths in unqualified patterns. use hir::{db::DefDatabase, AssocItem, ScopeDef}; +use ide_db::syntax_helpers::suggest_name; use syntax::ast::Pat; use crate::{ @@ -45,6 +46,19 @@ pub(crate) fn complete_pattern( return; } + // Suggest name only in let-stmt and fn param + if pattern_ctx.should_suggest_name { + let mut name_generator = suggest_name::NameGenerator::new(); + if let Some(suggested) = ctx + .expected_type + .as_ref() + .map(|ty| ty.strip_references()) + .and_then(|ty| name_generator.for_type(&ty, ctx.db, ctx.edition)) + { + acc.suggest_name(ctx, &suggested); + } + } + let refutable = pattern_ctx.refutability == PatternRefutability::Refutable; let single_variant_enum = |enum_: hir::Enum| ctx.db.enum_data(enum_.into()).variants.len() == 1; diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs index a632f148936d..d3579fd8cc6e 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/postfix.rs @@ -294,6 +294,18 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { let mut new_element_opt = initial_element.clone(); + while let Some(parent_deref_element) = + resulting_element.syntax().parent().and_then(ast::PrefixExpr::cast) + { + if parent_deref_element.op_kind() != Some(ast::UnaryOp::Deref) { + break; + } + + resulting_element = ast::Expr::from(parent_deref_element); + + new_element_opt = make::expr_prefix(syntax::T![*], new_element_opt); + } + if let Some(first_ref_expr) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { if let Some(expr) = first_ref_expr.expr() { resulting_element = expr; @@ -302,9 +314,10 @@ fn include_references(initial_element: &ast::Expr) -> (ast::Expr, ast::Expr) { while let Some(parent_ref_element) = resulting_element.syntax().parent().and_then(ast::RefExpr::cast) { + let exclusive = parent_ref_element.mut_token().is_some(); resulting_element = ast::Expr::from(parent_ref_element); - new_element_opt = make::expr_ref(new_element_opt, false); + new_element_opt = make::expr_ref(new_element_opt, exclusive); } } else { // If we do not find any ref expressions, restore @@ -855,4 +868,42 @@ fn test() { expect![[r#""#]], ); } + + #[test] + fn mut_ref_consuming() { + check_edit( + "call", + r#" +fn main() { + let mut x = &mut 2; + &mut x.$0; +} +"#, + r#" +fn main() { + let mut x = &mut 2; + ${1}(&mut x); +} +"#, + ); + } + + #[test] + fn deref_consuming() { + check_edit( + "call", + r#" +fn main() { + let mut x = &mut 2; + &mut *x.$0; +} +"#, + r#" +fn main() { + let mut x = &mut 2; + ${1}(&mut *x); +} +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs index d885b82ec90d..0d403f49b7a8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs @@ -19,6 +19,7 @@ pub struct CompletionConfig { pub term_search_fuel: u64, pub full_function_signatures: bool, pub callable: Option, + pub add_semicolon_to_unit: bool, pub snippet_cap: Option, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs index bcd9df941947..192f1b43face 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -4,7 +4,7 @@ mod analysis; #[cfg(test)] mod tests; -use std::iter; +use std::{iter, ops::ControlFlow}; use hir::{ HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo, @@ -15,7 +15,7 @@ use ide_db::{ }; use syntax::{ ast::{self, AttrKind, NameOrNameRef}, - AstNode, Edition, SmolStr, + match_ast, AstNode, Edition, SmolStr, SyntaxKind::{self, *}, SyntaxToken, TextRange, TextSize, T, }; @@ -26,7 +26,7 @@ use crate::{ CompletionConfig, }; -const COMPLETION_MARKER: &str = "intellijRulezz"; +const COMPLETION_MARKER: &str = "raCompletionMarker"; #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum PatternRefutability { @@ -264,6 +264,7 @@ pub(crate) struct PatternContext { pub(crate) refutability: PatternRefutability, pub(crate) param_ctx: Option, pub(crate) has_type_ascription: bool, + pub(crate) should_suggest_name: bool, pub(crate) parent_pat: Option, pub(crate) ref_token: Option, pub(crate) mut_token: Option, @@ -456,6 +457,16 @@ pub(crate) struct CompletionContext<'a> { /// /// Here depth will be 2 pub(crate) depth_from_crate_root: usize, + + /// Whether and how to complete semicolon for unit-returning functions. + pub(crate) complete_semicolon: CompleteSemicolon, +} + +#[derive(Debug)] +pub(crate) enum CompleteSemicolon { + DoNotComplete, + CompleteSemi, + CompleteComma, } impl CompletionContext<'_> { @@ -734,6 +745,53 @@ impl<'a> CompletionContext<'a> { let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count(); + let complete_semicolon = if config.add_semicolon_to_unit { + let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| { + match_ast! { + match ancestor { + ast::BlockExpr(_) => ControlFlow::Break(false), + ast::ClosureExpr(_) => ControlFlow::Break(true), + _ => ControlFlow::Continue(()) + } + } + }); + + if inside_closure_ret == ControlFlow::Break(true) { + CompleteSemicolon::DoNotComplete + } else { + let next_non_trivia_token = + std::iter::successors(token.next_token(), |it| it.next_token()) + .find(|it| !it.kind().is_trivia()); + let in_match_arm = token.parent_ancestors().try_for_each(|ancestor| { + if ast::MatchArm::can_cast(ancestor.kind()) { + ControlFlow::Break(true) + } else if matches!( + ancestor.kind(), + SyntaxKind::EXPR_STMT | SyntaxKind::BLOCK_EXPR + ) { + ControlFlow::Break(false) + } else { + ControlFlow::Continue(()) + } + }); + // FIXME: This will assume expr macros are not inside match, we need to somehow go to the "parent" of the root node. + let in_match_arm = match in_match_arm { + ControlFlow::Continue(()) => false, + ControlFlow::Break(it) => it, + }; + let complete_token = if in_match_arm { T![,] } else { T![;] }; + if next_non_trivia_token.map(|it| it.kind()) == Some(complete_token) { + CompleteSemicolon::DoNotComplete + } else if in_match_arm { + CompleteSemicolon::CompleteComma + } else { + CompleteSemicolon::CompleteSemi + } + } + } else { + CompleteSemicolon::DoNotComplete + }; + let ctx = CompletionContext { sema, scope, @@ -751,6 +809,7 @@ impl<'a> CompletionContext<'a> { qualifier_ctx, locals, depth_from_crate_root, + complete_semicolon, }; Some((ctx, analysis)) } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs index ed359394f1c4..1f9e3edf625d 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs @@ -1132,10 +1132,18 @@ fn classify_name_ref( ast::PathType(it) => make_path_kind_type(it.into()), ast::PathExpr(it) => { if let Some(p) = it.syntax().parent() { - if ast::ExprStmt::can_cast(p.kind()) { - if let Some(kind) = inbetween_body_and_decl_check(p) { - return Some(make_res(NameRefKind::Keyword(kind))); - } + let p_kind = p.kind(); + // The syntax node of interest, for which we want to check whether + // it is sandwiched between an item decl signature and its body. + let probe = if ast::ExprStmt::can_cast(p_kind) { + Some(p) + } else if ast::StmtList::can_cast(p_kind) { + Some(it.syntax().clone()) + } else { + None + }; + if let Some(kind) = probe.and_then(inbetween_body_and_decl_check) { + return Some(make_res(NameRefKind::Keyword(kind))); } } @@ -1199,7 +1207,13 @@ fn classify_name_ref( } } }, - ast::RecordExpr(it) => make_path_kind_expr(it.into()), + ast::RecordExpr(it) => { + // A record expression in this position is usually a result of parsing recovery, so check that + if let Some(kind) = inbetween_body_and_decl_check(it.syntax().clone()) { + return Some(make_res(NameRefKind::Keyword(kind))); + } + make_path_kind_expr(it.into()) + }, _ => return None, } }; @@ -1416,10 +1430,23 @@ fn pattern_context_for( _ => (None, None), }; + // Only suggest name in let-stmt or fn param + let should_suggest_name = matches!( + &pat, + ast::Pat::IdentPat(it) + if it.syntax() + .parent() + .map_or(false, |node| { + let kind = node.kind(); + ast::LetStmt::can_cast(kind) || ast::Param::can_cast(kind) + }) + ); + PatternContext { refutability, param_ctx, has_type_ascription, + should_suggest_name, parent_pat: pat.syntax().parent().and_then(ast::Pat::cast), mut_token, ref_token, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index a30a115da1f1..8c97ebd55003 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -19,8 +19,10 @@ use crate::{ }; /// `CompletionItem` describes a single completion entity which expands to 1 or more entries in the -/// editor pop-up. It is basically a POD with various properties. To construct a -/// [`CompletionItem`], use [`Builder::new`] method and the [`Builder`] struct. +/// editor pop-up. +/// +/// It is basically a POD with various properties. To construct a [`CompletionItem`], +/// use [`Builder::new`] method and the [`Builder`] struct. #[derive(Clone)] #[non_exhaustive] pub struct CompletionItem { @@ -129,7 +131,8 @@ impl fmt::Debug for CompletionItem { #[derive(Debug, Clone, Copy, Eq, PartialEq, Default)] pub struct CompletionRelevance { - /// This is set in cases like these: + /// This is set when the identifier being completed matches up with the name that is expected, + /// like in a function argument. /// /// ``` /// fn f(spam: String) {} @@ -139,9 +142,9 @@ pub struct CompletionRelevance { /// } /// ``` pub exact_name_match: bool, - /// See CompletionRelevanceTypeMatch doc comments for cases where this is set. + /// See [`CompletionRelevanceTypeMatch`]. pub type_match: Option, - /// This is set in cases like these: + /// Set for local variables. /// /// ``` /// fn foo(a: u32) { @@ -150,25 +153,26 @@ pub struct CompletionRelevance { /// } /// ``` pub is_local: bool, - /// This is set when trait items are completed in an impl of that trait. - pub is_item_from_trait: bool, - /// This is set for when trait items are from traits with `#[doc(notable_trait)]` - pub is_item_from_notable_trait: bool, - /// This is set when an import is suggested whose name is already imported. + /// Populated when the completion item comes from a trait (impl). + pub trait_: Option, + /// This is set when an import is suggested in a use item whose name is already imported. pub is_name_already_imported: bool, /// This is set for completions that will insert a `use` item. pub requires_import: bool, - /// Set for method completions of the `core::ops` and `core::cmp` family. - pub is_op_method: bool, /// Set for item completions that are private but in the workspace. pub is_private_editable: bool, /// Set for postfix snippet item completions pub postfix_match: Option, - /// This is set for type inference results - pub is_definite: bool, /// This is set for items that are function (associated or method) pub function: Option, } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct CompletionRelevanceTraitInfo { + /// The trait this item is from is a `#[doc(notable_trait)]` + pub notable_trait: bool, + /// Set for method completions of the `core::ops` and `core::cmp` family. + pub is_op_method: bool, +} #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum CompletionRelevanceTypeMatch { @@ -182,7 +186,7 @@ pub enum CompletionRelevanceTypeMatch { /// } /// ``` CouldUnify, - /// This is set in cases like these: + /// This is set in cases where the type matches the expected type, like: /// /// ``` /// fn f(spam: String) {} @@ -238,90 +242,82 @@ impl CompletionRelevance { /// See is_relevant if you need to make some judgement about score /// in an absolute sense. pub fn score(self) -> u32 { - let mut score = 0; + let mut score = !0 / 2; let CompletionRelevance { exact_name_match, type_match, is_local, - is_item_from_trait, is_name_already_imported, requires_import, - is_op_method, is_private_editable, postfix_match, - is_definite, - is_item_from_notable_trait, + trait_, function, } = self; + // only applicable for completions within use items + // lower rank for conflicting import names + if is_name_already_imported { + score -= 1; + } + // slightly prefer locals + if is_local { + score += 1; + } + // lower rank private things if !is_private_editable { score += 1; } - // lower rank trait op methods - if !is_op_method { - score += 10; + + if let Some(trait_) = trait_ { + // lower rank trait methods unless its notable + if !trait_.notable_trait { + score -= 5; + } + // lower rank trait op methods + if trait_.is_op_method { + score -= 5; + } } - // lower rank for conflicting import names - if !is_name_already_imported { - score += 1; - } - // lower rank for items that don't need an import - if !requires_import { - score += 1; + // lower rank for items that need an import + if requires_import { + score -= 1; } if exact_name_match { - score += 10; + score += 20; } - score += match postfix_match { - Some(CompletionRelevancePostfixMatch::Exact) => 100, - Some(CompletionRelevancePostfixMatch::NonExact) => 0, - None => 3, + match postfix_match { + Some(CompletionRelevancePostfixMatch::Exact) => score += 100, + Some(CompletionRelevancePostfixMatch::NonExact) => score -= 5, + None => (), }; score += match type_match { - Some(CompletionRelevanceTypeMatch::Exact) => 8, - Some(CompletionRelevanceTypeMatch::CouldUnify) => 3, + Some(CompletionRelevanceTypeMatch::Exact) => 18, + Some(CompletionRelevanceTypeMatch::CouldUnify) => 5, None => 0, }; - // slightly prefer locals - if is_local { - score += 1; - } - if is_item_from_trait { - score += 1; - } - if is_item_from_notable_trait { - score += 1; - } - if is_definite { - score += 10; - } + if let Some(function) = function { + let mut fn_score = match function.return_type { + CompletionRelevanceReturnType::DirectConstructor => 15, + CompletionRelevanceReturnType::Builder => 10, + CompletionRelevanceReturnType::Constructor => 5, + CompletionRelevanceReturnType::Other => 0u32, + }; - score += function - .map(|asf| { - let mut fn_score = match asf.return_type { - CompletionRelevanceReturnType::DirectConstructor => 15, - CompletionRelevanceReturnType::Builder => 10, - CompletionRelevanceReturnType::Constructor => 5, - CompletionRelevanceReturnType::Other => 0, - }; + // When a fn is bumped due to return type: + // Bump Constructor or Builder methods with no arguments, + // over them than with self arguments + if function.has_params { + // bump associated functions + fn_score = fn_score.saturating_sub(1); + } else if function.has_self_param { + // downgrade methods (below Constructor) + fn_score = fn_score.min(1); + } - // When a fn is bumped due to return type: - // Bump Constructor or Builder methods with no arguments, - // over them than with self arguments - if fn_score > 0 { - if !asf.has_params { - // bump associated functions - fn_score += 1; - } else if asf.has_self_param { - // downgrade methods (below Constructor) - fn_score = 1; - } - } - - fn_score - }) - .unwrap_or_default(); + score += fn_score; + }; score } @@ -364,6 +360,7 @@ impl CompletionItemKind { SymbolKind::Field => "fd", SymbolKind::Function => "fn", SymbolKind::Impl => "im", + SymbolKind::InlineAsmRegOrRegClass => "ar", SymbolKind::Label => "lb", SymbolKind::LifetimeParam => "lt", SymbolKind::Local => "lc", @@ -701,8 +698,21 @@ mod tests { // that any items in the same vec have the same score. let expected_relevance_order = vec![ vec![], - vec![Cr { is_op_method: true, is_private_editable: true, ..default }], - vec![Cr { is_op_method: true, ..default }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + is_private_editable: true, + ..default + }], + vec![Cr { + trait_: Some(crate::item::CompletionRelevanceTraitInfo { + notable_trait: false, + is_op_method: true, + }), + ..default + }], vec![Cr { postfix_match: Some(CompletionRelevancePostfixMatch::NonExact), ..default }], vec![Cr { is_private_editable: true, ..default }], vec![default], diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs index ff5ec3a29f3e..f2e9de9382c6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render.rs @@ -249,7 +249,11 @@ pub(crate) fn render_type_inference( ty_string, ctx.edition, ); - builder.set_relevance(CompletionRelevance { is_definite: true, ..Default::default() }); + builder.set_relevance(CompletionRelevance { + type_match: Some(CompletionRelevanceTypeMatch::Exact), + exact_name_match: true, + ..Default::default() + }); builder.build(ctx.db) } @@ -756,7 +760,7 @@ mod tests { relevance.postfix_match == Some(CompletionRelevancePostfixMatch::Exact), "snippet", ), - (relevance.is_op_method, "op_method"), + (relevance.trait_.map_or(false, |it| it.is_op_method), "op_method"), (relevance.requires_import, "requires_import"), ] .into_iter() @@ -1181,7 +1185,7 @@ fn main() { fo$0 } label: "main()", source_range: 68..70, delete: 68..70, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1240,7 +1244,7 @@ fn main() { let _: m::Spam = S$0 } label: "main()", source_range: 75..76, delete: 75..76, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1272,14 +1276,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1300,14 +1301,11 @@ fn main() { let _: m::Spam = S$0 } Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, trigger_call_info: true, @@ -1333,7 +1331,7 @@ fn main() { som$0 } label: "main()", source_range: 56..59, delete: 56..59, - insert: "main()$0", + insert: "main();$0", kind: SymbolKind( Function, ), @@ -1344,7 +1342,7 @@ fn main() { som$0 } label: "something_deprecated()", source_range: 56..59, delete: 56..59, - insert: "something_deprecated()$0", + insert: "something_deprecated();$0", kind: SymbolKind( Function, ), @@ -1380,14 +1378,11 @@ fn foo() { A { the$0 } } CouldUnify, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -1418,7 +1413,7 @@ impl S { label: "bar()", source_range: 94..94, delete: 94..94, - insert: "bar()$0", + insert: "bar();$0", kind: SymbolKind( Method, ), @@ -1431,14 +1426,11 @@ impl S { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1548,7 +1540,7 @@ fn foo(s: S) { s.$0 } label: "the_method()", source_range: 81..81, delete: 81..81, - insert: "the_method()$0", + insert: "the_method();$0", kind: SymbolKind( Method, ), @@ -1558,14 +1550,11 @@ fn foo(s: S) { s.$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -1774,14 +1763,11 @@ fn f() -> i32 { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2492,14 +2478,11 @@ fn foo(f: Foo) { let _: &u32 = f.b$0 } exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: true, @@ -2574,14 +2557,11 @@ fn foo() { Exact, ), is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -2624,14 +2604,11 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: false, + trait_: None, is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: Some( CompletionRelevanceFn { has_params: false, @@ -2812,7 +2789,7 @@ fn main() { r#" mod m { pub fn r#type {} } fn main() { - m::r#type()$0 + m::r#type();$0 } "#, ) @@ -2986,7 +2963,7 @@ fn main() { label: "flush()", source_range: 193..193, delete: 193..193, - insert: "flush()$0", + insert: "flush();$0", kind: SymbolKind( Method, ), @@ -2996,14 +2973,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, @@ -3011,7 +2990,7 @@ fn main() { label: "write()", source_range: 193..193, delete: 193..193, - insert: "write()$0", + insert: "write();$0", kind: SymbolKind( Method, ), @@ -3021,14 +3000,16 @@ fn main() { exact_name_match: false, type_match: None, is_local: false, - is_item_from_trait: false, - is_item_from_notable_trait: true, + trait_: Some( + CompletionRelevanceTraitInfo { + notable_trait: true, + is_op_method: false, + }, + ), is_name_already_imported: false, requires_import: false, - is_op_method: false, is_private_editable: false, postfix_match: None, - is_definite: false, function: None, }, }, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs index 74092b53f523..a859d79e2433 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/render/function.rs @@ -7,10 +7,12 @@ use stdx::{format_to, to_lower_snake_case}; use syntax::{format_smolstr, AstNode, Edition, SmolStr, ToSmolStr}; use crate::{ - context::{CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind}, + context::{ + CompleteSemicolon, CompletionContext, DotAccess, DotAccessKind, PathCompletionCtx, PathKind, + }, item::{ Builder, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevanceFn, - CompletionRelevanceReturnType, + CompletionRelevanceReturnType, CompletionRelevanceTraitInfo, }, render::{ compute_exact_name_match, compute_ref_match, compute_type_match, match_types, RenderContext, @@ -88,11 +90,13 @@ fn render( let ret_type = func.ret_type(db); let assoc_item = func.as_assoc_item(db); - let trait_ = assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)); - let is_op_method = trait_.map_or(false, |trait_| completion.is_ops_trait(trait_)); - - let is_item_from_notable_trait = - trait_.map_or(false, |trait_| completion.is_doc_notable_trait(trait_)); + let trait_info = + assoc_item.and_then(|trait_| trait_.container_or_implemented_trait(db)).map(|trait_| { + CompletionRelevanceTraitInfo { + notable_trait: completion.is_doc_notable_trait(trait_), + is_op_method: completion.is_ops_trait(trait_), + } + }); let (has_dot_receiver, has_call_parens, cap) = match func_kind { FuncKind::Function(&PathCompletionCtx { @@ -129,8 +133,7 @@ fn render( }, exact_name_match: compute_exact_name_match(completion, &call), function, - is_op_method, - is_item_from_notable_trait, + trait_: trait_info, ..ctx.completion_relevance() }); @@ -159,7 +162,16 @@ fn render( .lookup_by(name.unescaped().display(db).to_smolstr()); if let Some((cap, (self_param, params))) = complete_call_parens { - add_call_parens(&mut item, completion, cap, call, escaped_call, self_param, params); + add_call_parens( + &mut item, + completion, + cap, + call, + escaped_call, + self_param, + params, + &ret_type, + ); } match ctx.import_to_add { @@ -216,10 +228,11 @@ pub(super) fn add_call_parens<'b>( escaped_name: SmolStr, self_param: Option, params: Vec, + ret_type: &hir::Type, ) -> &'b mut Builder { cov_mark::hit!(inserts_parens_for_function_calls); - let (snippet, label_suffix) = if self_param.is_none() && params.is_empty() { + let (mut snippet, label_suffix) = if self_param.is_none() && params.is_empty() { (format!("{escaped_name}()$0"), "()") } else { builder.trigger_call_info(); @@ -264,6 +277,24 @@ pub(super) fn add_call_parens<'b>( (snippet, "(…)") }; + if ret_type.is_unit() { + match ctx.complete_semicolon { + CompleteSemicolon::DoNotComplete => {} + CompleteSemicolon::CompleteSemi | CompleteSemicolon::CompleteComma => { + cov_mark::hit!(complete_semicolon); + let ch = if matches!(ctx.complete_semicolon, CompleteSemicolon::CompleteComma) { + ',' + } else { + ';' + }; + if snippet.ends_with("$0") { + snippet.insert(snippet.len() - "$0".len(), ch); + } else { + snippet.push(ch); + } + } + } + } builder.label(SmolStr::from_iter([&name, label_suffix])).insert_snippet(cap, snippet) } @@ -392,7 +423,7 @@ fn main() { no_$0 } "#, r#" fn no_args() {} -fn main() { no_args()$0 } +fn main() { no_args();$0 } "#, ); @@ -404,7 +435,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args(${1:x}, ${2:y})$0 } +fn main() { with_args(${1:x}, ${2:y});$0 } "#, ); @@ -413,14 +444,14 @@ fn main() { with_args(${1:x}, ${2:y})$0 } r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.f$0 } "#, r#" struct S; impl S { - fn foo(&self) {} + fn foo(&self) -> i32 { 0 } } fn bar(s: &S) { s.foo()$0 } "#, @@ -443,7 +474,7 @@ impl S { fn foo(&self, x: i32) {} } fn bar(s: &S) { - s.foo(${1:x})$0 + s.foo(${1:x});$0 } "#, ); @@ -462,7 +493,7 @@ impl S { struct S {} impl S { fn foo(&self, x: i32) { - self.foo(${1:x})$0 + self.foo(${1:x});$0 } } "#, @@ -485,7 +516,7 @@ struct S; impl S { fn foo(&self) {} } -fn main() { S::foo(${1:&self})$0 } +fn main() { S::foo(${1:&self});$0 } "#, ); } @@ -502,7 +533,7 @@ fn main() { with_$0 } "#, r#" fn with_args(x: i32, y: String) {} -fn main() { with_args($0) } +fn main() { with_args($0); } "#, ); } @@ -517,7 +548,7 @@ fn main() { f$0 } "#, r#" fn foo(_foo: i32, ___bar: bool, ho_ge_: String) {} -fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_})$0 } +fn main() { foo(${1:foo}, ${2:bar}, ${3:ho_ge_});$0 } "#, ); } @@ -539,7 +570,7 @@ struct Foo {} fn ref_arg(x: &Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&x})$0 + ref_arg(${1:&x});$0 } "#, ); @@ -562,7 +593,7 @@ struct Foo {} fn ref_arg(x: &mut Foo) {} fn main() { let x = Foo {}; - ref_arg(${1:&mut x})$0 + ref_arg(${1:&mut x});$0 } "#, ); @@ -595,7 +626,7 @@ impl Bar { fn main() { let x = Foo {}; let y = Bar {}; - y.apply_foo(${1:&x})$0 + y.apply_foo(${1:&x});$0 } "#, ); @@ -616,7 +647,7 @@ fn main() { fn take_mutably(mut x: &i32) {} fn main() { - take_mutably(${1:x})$0 + take_mutably(${1:x});$0 } "#, ); @@ -649,7 +680,7 @@ fn qux(Foo { bar }: Foo) { } fn main() { - qux(${1:foo})$0 + qux(${1:foo});$0 } "#, ); @@ -735,6 +766,136 @@ fn g(foo: ()#[baz = "qux"] mut ba$0) r#" fn f(foo: (), #[baz = "qux"] mut bar: u32) {} fn g(foo: (), #[baz = "qux"] mut bar: u32) +"#, + ); + } + + #[test] + fn complete_semicolon_for_unit() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo() {} +fn bar() { + foo();$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a});$0 +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0; +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0; +} +"#, + ); + check_edit_with_config( + CompletionConfig { add_semicolon_to_unit: false, ..TEST_CONFIG }, + r#"foo"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo$0 +} +"#, + r#" +fn foo(a: i32) {} +fn bar() { + foo(${1:a})$0 +} +"#, + ); + } + + #[test] + fn complete_comma_for_unit_match_arm() { + cov_mark::check!(complete_semicolon); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0 + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo(),$0 + } +} +"#, + ); + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => fo$0, + } +} +"#, + r#" +fn foo() {} +fn bar() { + match Some(false) { + v => foo()$0, + } +} +"#, + ); + } + + #[test] + fn no_semicolon_in_closure_ret() { + check_edit( + r#"foo"#, + r#" +fn foo() {} +fn baz(_: impl FnOnce()) {} +fn bar() { + baz(|| fo$0); +} +"#, + r#" +fn foo() {} +fn baz(_: impl FnOnce()) {} +fn bar() { + baz(|| foo()$0); +} "#, ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs index 04ba7e1f41b6..9d77d9700718 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs @@ -70,6 +70,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig { term_search_fuel: 200, full_function_signatures: false, callable: Some(CallableSnippets::FillArguments), + add_semicolon_to_unit: true, snippet_cap: SnippetCap::new(true), insert_use: InsertUseConfig { granularity: ImportGranularity::Crate, diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs index 351abe9850b9..1bbe097cc6c8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs @@ -26,6 +26,7 @@ struct Foo; at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -75,6 +76,7 @@ fn with_existing_attr() { at cfg(…) at cfg_attr(…) at deny(…) + at expect(…) at forbid(…) at warn(…) kw crate:: @@ -97,6 +99,7 @@ fn attr_on_source_file() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at feature(…) at forbid(…) at must_use @@ -127,6 +130,7 @@ fn attr_on_module() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_use at must_use @@ -149,6 +153,7 @@ fn attr_on_module() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_implicit_prelude @@ -174,6 +179,7 @@ fn attr_on_macro_rules() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_export at macro_use @@ -199,6 +205,7 @@ fn attr_on_macro_def() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -222,6 +229,7 @@ fn attr_on_extern_crate() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at macro_use at must_use @@ -246,6 +254,7 @@ fn attr_on_use() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -269,6 +278,7 @@ fn attr_on_type_alias() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -299,6 +309,7 @@ struct Foo; at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -326,6 +337,7 @@ fn attr_on_enum() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -351,6 +363,7 @@ fn attr_on_const() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -374,6 +387,7 @@ fn attr_on_static() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at global_allocator @@ -402,6 +416,7 @@ fn attr_on_trait() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at must_use @@ -427,6 +442,7 @@ fn attr_on_impl() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -446,6 +462,7 @@ fn attr_on_impl() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at must_use at no_mangle @@ -469,6 +486,7 @@ fn attr_on_extern_block() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at link at must_use @@ -489,6 +507,7 @@ fn attr_on_extern_block() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at forbid(…) at link at must_use @@ -509,6 +528,7 @@ fn attr_on_variant() { at cfg(…) at cfg_attr(…) at deny(…) + at expect(…) at forbid(…) at non_exhaustive at warn(…) @@ -532,6 +552,7 @@ fn attr_on_fn() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at ignore = "…" @@ -572,6 +593,7 @@ fn attr_in_source_file_end() { at doc = "…" at doc(alias = "…") at doc(hidden) + at expect(…) at export_name = "…" at forbid(…) at global_allocator diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 8350fdcc4c07..0b532064fb2b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -53,7 +53,7 @@ fn main() { use dep::io::stdin; fn main() { - stdin()$0 + stdin();$0 } "#, ); @@ -274,7 +274,7 @@ fn trait_function_fuzzy_completion() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::weird_function()$0 + dep::test_mod::TestStruct::weird_function();$0 } "#, ); @@ -368,7 +368,7 @@ use dep::test_mod::TestTrait; fn main() { let test_struct = dep::test_mod::TestStruct {}; - test_struct.random_method()$0 + test_struct.random_method();$0 } "#, ); @@ -419,7 +419,7 @@ impl foo::TestTrait for fundamental::Box { fn main() { let t = fundamental::Box(TestStruct); - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -466,7 +466,7 @@ impl foo::TestTrait for &TestStruct { fn main() { let t = &TestStruct; - t.some_method()$0 + t.some_method();$0 } "#, ); @@ -507,7 +507,7 @@ fn completion(whatever: T) { use foo::{NotInScope, Wrapper}; fn completion(whatever: T) { - whatever.inner().not_in_scope()$0 + whatever.inner().not_in_scope();$0 } "#, ); @@ -579,7 +579,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestAlias::random_method()$0 + dep::test_mod::TestAlias::random_method();$0 } "#, ); @@ -702,7 +702,7 @@ fn main() { use dep::test_mod::TestTrait; fn main() { - dep::test_mod::TestStruct::another_function()$0 + dep::test_mod::TestStruct::another_function();$0 } "#, ); diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs index 8aad7bfc3adc..532d4928eff9 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs @@ -313,6 +313,7 @@ impl Test for () { ct const CONST1: () = fn async fn function2() fn fn function1() + fn fn function2() ma makro!(…) macro_rules! makro md module ta type Type1 = diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs index 6a0b67e291af..bd3e7c72bcd6 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/pattern.rs @@ -198,6 +198,7 @@ fn foo(a$0: Tuple) { st Unit bn Record {…} Record { field$1 }$0 bn Tuple(…) Tuple($1)$0 + bn tuple kw mut kw ref "#]], @@ -850,3 +851,75 @@ fn foo() { "#, ); } + +#[test] +fn suggest_name_for_pattern() { + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = S1; +} +"#, + ); + + check_edit( + "s1", + r#" +struct S1; + +fn foo(s$0: S1) { +} +"#, + r#" +struct S1; + +fn foo(s1: S1) { +} +"#, + ); + + // Tests for &adt + check_edit( + "s1", + r#" +struct S1; + +fn foo() { + let $0 = &S1; +} +"#, + r#" +struct S1; + +fn foo() { + let s1 = &S1; +} +"#, + ); + + // Do not suggest reserved keywords + check_empty( + r#" +struct Struct; + +fn foo() { + let $0 = Struct; +} +"#, + expect![[r#" + st Struct + kw mut + kw ref + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs index bc630189edc9..d81b3d697aa8 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/raw_identifiers.rs @@ -30,7 +30,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); @@ -45,7 +45,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::dyn()$0 + a::dyn();$0 "#]], ); } @@ -63,7 +63,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); @@ -78,7 +78,7 @@ fn foo() { "#, expect![[r#" fn foo() { - a::r#dyn()$0 + a::r#dyn();$0 "#]], ); } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs index 2ae7d37889e5..508f6248dd41 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/special.rs @@ -63,7 +63,7 @@ fn _alpha() {} "#, r#" fn main() { - _alpha()$0 + _alpha();$0 } fn _alpha() {} "#, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs index 5d4b1999088a..099f26eba78a 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/defs.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/defs.rs @@ -10,8 +10,8 @@ use either::Either; use hir::{ Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType, Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field, - Function, GenericParam, HasVisibility, HirDisplay, Impl, Label, Local, Macro, Module, - ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, + Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro, + Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility, }; use span::Edition; @@ -50,6 +50,8 @@ pub enum Definition { BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), ExternCrateDecl(ExternCrateDecl), + InlineAsmRegOrRegClass(()), + InlineAsmOperand(InlineAsmOperand), } impl Definition { @@ -83,11 +85,13 @@ impl Definition { Definition::Label(it) => it.module(db), Definition::ExternCrateDecl(it) => it.module(db), Definition::DeriveHelper(it) => it.derive().module(db), + Definition::InlineAsmOperand(it) => it.parent(db).module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::TupleField(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, }; Some(module) } @@ -121,7 +125,9 @@ impl Definition { | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, }; Some(vis) } @@ -150,6 +156,8 @@ impl Definition { Definition::ToolModule(_) => return None, // FIXME Definition::DeriveHelper(it) => it.name(db), Definition::ExternCrateDecl(it) => return it.alias_or_name(db), + Definition::InlineAsmRegOrRegClass(_) => return None, + Definition::InlineAsmOperand(op) => return op.name(db), }; Some(name) } @@ -212,6 +220,7 @@ impl Definition { Definition::ToolModule(_) => None, Definition::DeriveHelper(_) => None, Definition::TupleField(_) => None, + Definition::InlineAsmRegOrRegClass(_) | Definition::InlineAsmOperand(_) => None, }; docs.or_else(|| { @@ -268,6 +277,9 @@ impl Definition { Definition::DeriveHelper(it) => { format!("derive_helper {}", it.name(db).display(db, edition)) } + // FIXME + Definition::InlineAsmRegOrRegClass(_) => "inline_asm_reg_or_reg_class".to_owned(), + Definition::InlineAsmOperand(_) => "inline_asm_reg_operand".to_owned(), } } } @@ -429,7 +441,6 @@ impl NameClass { let _p = tracing::info_span!("NameClass::classify").entered(); let parent = name.syntax().parent()?; - let definition = match_ast! { match parent { ast::Item(it) => classify_item(sema, it)?, @@ -440,6 +451,7 @@ impl NameClass { ast::Variant(it) => Definition::Variant(sema.to_def(&it)?), ast::TypeParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), ast::ConstParam(it) => Definition::GenericParam(sema.to_def(&it)?.into()), + ast::AsmOperandNamed(it) => Definition::InlineAsmOperand(sema.to_def(&it)?), _ => return None, } }; @@ -699,6 +711,9 @@ impl NameRefClass { NameRefClass::ExternCrateShorthand { krate, decl: extern_crate } }) }, + ast::AsmRegSpec(_) => { + Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()))) + }, _ => None } } @@ -753,6 +768,18 @@ impl From for Definition { } } +impl From for Definition { + fn from(value: InlineAsmOperand) -> Self { + Definition::InlineAsmOperand(value) + } +} + +impl From> for Definition { + fn from(value: Either) -> Self { + value.either(Definition::from, Definition::from) + } +} + impl AsAssocItem for Definition { fn as_assoc_item(self, db: &dyn hir::db::HirDatabase) -> Option { match self { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 3cf29987fa14..a45ff9a95455 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -36,8 +36,9 @@ pub mod generated { pub mod syntax_helpers { pub mod format_string; pub mod format_string_exprs; - pub mod insert_whitespace_into_node; + pub use hir::prettify_macro_expansion; pub mod node_ext; + pub mod suggest_name; pub use parser::LexedStr; } @@ -223,6 +224,7 @@ pub enum SymbolKind { Function, Method, Impl, + InlineAsmRegOrRegClass, Label, LifetimeParam, Local, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs index 262eefeec00e..f1404ed9f22c 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/rename.rs @@ -200,12 +200,14 @@ impl Definition { .and_then(syn_ctx_is_root) } } + Definition::InlineAsmOperand(it) => name_range(it, sema).and_then(syn_ctx_is_root), Definition::BuiltinType(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) | Definition::SelfType(_) | Definition::ToolModule(_) - | Definition::TupleField(_) => return None, + | Definition::TupleField(_) + | Definition::InlineAsmRegOrRegClass(_) => return None, // FIXME: This should be doable in theory Definition::DeriveHelper(_) => return None, }; diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs index 12ce5a403fe8..4166b08339bf 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs @@ -8,17 +8,18 @@ use std::mem; use std::{cell::LazyCell, cmp::Reverse}; use base_db::{salsa::Database, SourceDatabase, SourceRootDatabase}; +use either::Either; use hir::{ sym, Adt, AsAssocItem, DefWithBody, FileRange, FileRangeWrapper, HasAttrs, HasContainer, - HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, ItemContainer, ModuleSource, - PathResolution, Semantics, Visibility, + HasSource, HirFileIdExt, InFile, InFileWrapper, InRealFile, InlineAsmOperand, ItemContainer, + ModuleSource, PathResolution, Semantics, Visibility, }; use memchr::memmem::Finder; use parser::SyntaxKind; use rustc_hash::{FxHashMap, FxHashSet}; use span::EditionedFileId; use syntax::{ - ast::{self, HasName}, + ast::{self, HasName, Rename}, match_ast, AstNode, AstToken, SmolStr, SyntaxElement, SyntaxNode, TextRange, TextSize, ToSmolStr, }; @@ -318,6 +319,23 @@ impl Definition { }; } + if let Definition::InlineAsmOperand(op) = self { + let def = match op.parent(db) { + DefWithBody::Function(f) => f.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Const(c) => c.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Static(s) => s.source(db).map(|src| src.syntax().cloned()), + DefWithBody::Variant(v) => v.source(db).map(|src| src.syntax().cloned()), + // FIXME: implement + DefWithBody::InTypeConst(_) => return SearchScope::empty(), + }; + return match def { + Some(def) => SearchScope::file_range( + def.as_ref().original_file_range_with_macro_call_body(db), + ), + None => SearchScope::single_file(file_id), + }; + } + if let Definition::SelfType(impl_) = self { return match impl_.source(db).map(|src| src.syntax().cloned()) { Some(def) => SearchScope::file_range( @@ -387,6 +405,7 @@ impl Definition { pub fn usages<'a>(self, sema: &'a Semantics<'_, RootDatabase>) -> FindUsages<'a> { FindUsages { def: self, + rename: None, assoc_item_container: self.as_assoc_item(sema.db).map(|a| a.container(sema.db)), sema, scope: None, @@ -399,6 +418,7 @@ impl Definition { #[derive(Clone)] pub struct FindUsages<'a> { def: Definition, + rename: Option<&'a Rename>, sema: &'a Semantics<'a, RootDatabase>, scope: Option<&'a SearchScope>, /// The container of our definition should it be an assoc item @@ -429,6 +449,14 @@ impl<'a> FindUsages<'a> { self } + // FIXME: This is just a temporary fix for not handling import aliases like + // `use Foo as Bar`. We need to support them in a proper way. + // See issue #14079 + pub fn with_rename(mut self, rename: Option<&'a Rename>) -> Self { + self.rename = rename; + self + } + pub fn at_least_one(&self) -> bool { let mut found = false; self.search(&mut |_, _| { @@ -866,9 +894,16 @@ impl<'a> FindUsages<'a> { } }; - let name = match self.def { + let name = match (self.rename, self.def) { + (Some(rename), _) => { + if rename.underscore_token().is_some() { + None + } else { + rename.name().map(|n| n.to_smolstr()) + } + } // special case crate modules as these do not have a proper name - Definition::Module(module) if module.is_crate_root() => { + (_, Definition::Module(module)) if module.is_crate_root() => { // FIXME: This assumes the crate name is always equal to its display name when it // really isn't // we should instead look at the dependency edge name and recursively search our way @@ -908,7 +943,6 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new(name); let include_self_kw_refs = self.include_self_kw_refs.as_ref().map(|ty| (ty, Finder::new("Self"))); - for (text, file_id, search_range) in Self::scope_files(sema.db, &search_scope) { self.sema.db.unwind_if_cancelled(); let tree = LazyCell::new(move || sema.parse(file_id).syntax().clone()); @@ -917,7 +951,7 @@ impl<'a> FindUsages<'a> { for offset in Self::match_indices(&text, finder, search_range) { tree.token_at_offset(offset).for_each(|token| { let Some(str_token) = ast::String::cast(token.clone()) else { return }; - if let Some((range, nameres)) = + if let Some((range, Some(nameres))) = sema.check_for_format_args_template(token, offset) { if self.found_format_args_ref(file_id, range, str_token, nameres, sink) {} @@ -1087,19 +1121,19 @@ impl<'a> FindUsages<'a> { file_id: EditionedFileId, range: TextRange, token: ast::String, - res: Option, + res: Either, sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool, ) -> bool { - match res.map(Definition::from) { - Some(def) if def == self.def => { - let reference = FileReference { - range, - name: FileReferenceNode::FormatStringEntry(token, range), - category: ReferenceCategory::READ, - }; - sink(file_id, reference) - } - _ => false, + let def = res.either(Definition::from, Definition::from); + if def == self.def { + let reference = FileReference { + range, + name: FileReferenceNode::FormatStringEntry(token, range), + category: ReferenceCategory::READ, + }; + sink(file_id, reference) + } else { + false } } diff --git a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs index a83f8473c396..73073e92f787 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/source_change.rs @@ -9,10 +9,13 @@ use crate::{assists::Command, SnippetCap}; use base_db::AnchoredPathBuf; use itertools::Itertools; use nohash_hasher::IntMap; +use rustc_hash::FxHashMap; use span::FileId; use stdx::never; use syntax::{ - algo, AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, + algo, + syntax_editor::{SyntaxAnnotation, SyntaxEditor}, + AstNode, SyntaxElement, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize, }; use text_edit::{TextEdit, TextEditBuilder}; @@ -197,6 +200,11 @@ pub struct SourceChangeBuilder { pub source_change: SourceChange, pub command: Option, + /// Keeps track of all edits performed on each file + pub file_editors: FxHashMap, + /// Keeps track of which annotations correspond to which snippets + pub snippet_annotations: Vec<(AnnotationSnippet, SyntaxAnnotation)>, + /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. pub mutated_tree: Option, /// Keeps track of where to place snippets @@ -238,6 +246,8 @@ impl SourceChangeBuilder { file_id: file_id.into(), source_change: SourceChange::default(), command: None, + file_editors: FxHashMap::default(), + snippet_annotations: vec![], mutated_tree: None, snippet_builder: None, } @@ -248,7 +258,75 @@ impl SourceChangeBuilder { self.file_id = file_id.into(); } + pub fn make_editor(&self, node: &SyntaxNode) -> SyntaxEditor { + SyntaxEditor::new(node.ancestors().last().unwrap_or_else(|| node.clone())) + } + + pub fn add_file_edits(&mut self, file_id: impl Into, edit: SyntaxEditor) { + match self.file_editors.entry(file_id.into()) { + Entry::Occupied(mut entry) => entry.get_mut().merge(edit), + Entry::Vacant(entry) => { + entry.insert(edit); + } + } + } + + pub fn make_placeholder_snippet(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::Over) + } + + pub fn make_tabstop_before(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::Before) + } + + pub fn make_tabstop_after(&mut self, _cap: SnippetCap) -> SyntaxAnnotation { + self.add_snippet_annotation(AnnotationSnippet::After) + } + fn commit(&mut self) { + // Apply syntax editor edits + for (file_id, editor) in mem::take(&mut self.file_editors) { + let edit_result = editor.finish(); + let mut snippet_edit = vec![]; + + // Find snippet edits + for (kind, annotation) in &self.snippet_annotations { + let elements = edit_result.find_annotation(*annotation); + + let snippet = match (kind, elements) { + (AnnotationSnippet::Before, [element]) => { + Snippet::Tabstop(element.text_range().start()) + } + (AnnotationSnippet::After, [element]) => { + Snippet::Tabstop(element.text_range().end()) + } + (AnnotationSnippet::Over, [element]) => { + Snippet::Placeholder(element.text_range()) + } + (AnnotationSnippet::Over, elements) if !elements.is_empty() => { + Snippet::PlaceholderGroup( + elements.iter().map(|it| it.text_range()).collect(), + ) + } + _ => continue, + }; + + snippet_edit.push(snippet); + } + + let mut edit = TextEdit::builder(); + algo::diff(edit_result.old_root(), edit_result.new_root()).into_text_edit(&mut edit); + let edit = edit.finish(); + + let snippet_edit = + if !snippet_edit.is_empty() { Some(SnippetEdit::new(snippet_edit)) } else { None }; + + if !edit.is_empty() || snippet_edit.is_some() { + self.source_change.insert_source_and_snippet_edit(file_id, edit, snippet_edit); + } + } + + // Apply mutable edits let snippet_edit = self.snippet_builder.take().map(|builder| { SnippetEdit::new( builder.places.into_iter().flat_map(PlaceSnippet::finalize_position).collect(), @@ -369,6 +447,13 @@ impl SourceChangeBuilder { self.source_change.is_snippet = true; } + fn add_snippet_annotation(&mut self, kind: AnnotationSnippet) -> SyntaxAnnotation { + let annotation = SyntaxAnnotation::new(); + self.snippet_annotations.push((kind, annotation)); + self.source_change.is_snippet = true; + annotation + } + pub fn finish(mut self) -> SourceChange { self.commit(); @@ -416,6 +501,15 @@ pub enum Snippet { PlaceholderGroup(Vec), } +pub enum AnnotationSnippet { + /// Place a tabstop before an element + Before, + /// Place a tabstop before an element + After, + /// Place a placeholder snippet in place of the element(s) + Over, +} + enum PlaceSnippet { /// Place a tabstop before an element Before(SyntaxElement), diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs similarity index 70% rename from src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs rename to src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs index 3130ef069557..2679cbef61b3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/utils/suggest_name.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/suggest_name.rs @@ -1,14 +1,18 @@ //! This module contains functions to suggest names for expressions, functions and other items +use std::{collections::hash_map::Entry, str::FromStr}; + use hir::Semantics; -use ide_db::{FxHashSet, RootDatabase}; use itertools::Itertools; +use rustc_hash::FxHashMap; use stdx::to_lower_snake_case; use syntax::{ ast::{self, HasName}, - match_ast, AstNode, Edition, SmolStr, + match_ast, AstNode, Edition, SmolStr, SmolStrBuilder, }; +use crate::RootDatabase; + /// Trait names, that will be ignored when in `impl Trait` and `dyn Trait` const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "PartialEq"]; @@ -16,7 +20,9 @@ const USELESS_TRAITS: &[&str] = &["Send", "Sync", "Copy", "Clone", "Eq", "Partia /// /// **NOTE**: they all must be snake lower case const USELESS_NAMES: &[&str] = - &["new", "default", "option", "some", "none", "ok", "err", "str", "string"]; + &["new", "default", "option", "some", "none", "ok", "err", "str", "string", "from", "into"]; + +const USELESS_NAME_PREFIXES: &[&str] = &["from_", "with_", "into_"]; /// Generic types replaced by their first argument /// @@ -58,59 +64,131 @@ const USELESS_METHODS: &[&str] = &[ "into_future", ]; -/// Suggest a unique name for generic parameter. +/// Generator for new names /// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. +/// The generator keeps track of existing names and suggests new names that do +/// not conflict with existing names. /// -/// The function checks if the name conflicts with existing generic parameters. -/// If so, it will try to resolve the conflict by adding a number suffix, e.g. -/// `T`, `T0`, `T1`, ... -pub(crate) fn for_unique_generic_name( - name: &str, - existing_params: &ast::GenericParamList, -) -> SmolStr { - let param_names = existing_params - .generic_params() - .map(|param| match param { - ast::GenericParam::TypeParam(t) => t.name().unwrap().to_string(), - p => p.to_string(), - }) - .collect::>(); - let mut name = name.to_owned(); - let base_len = name.len(); - let mut count = 0; - while param_names.contains(&name) { - name.truncate(base_len); - name.push_str(&count.to_string()); - count += 1; - } - - name.into() +/// The generator will try to resolve conflicts by adding a numeric suffix to +/// the name, e.g. `a`, `a1`, `a2`, ... +/// +/// # Examples +/// ```rust +/// let mut generator = NameGenerator::new(); +/// assert_eq!(generator.suggest_name("a"), "a"); +/// assert_eq!(generator.suggest_name("a"), "a1"); +/// +/// assert_eq!(generator.suggest_name("b2"), "b2"); +/// assert_eq!(generator.suggest_name("b"), "b3"); +/// ``` +#[derive(Debug, Default)] +pub struct NameGenerator { + pool: FxHashMap, } -/// Suggest name of impl trait type -/// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. -/// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from the first -/// character of the name for the first type bound. -/// -/// If the name conflicts with existing generic parameters, it will try to -/// resolve the conflict with `for_unique_generic_name`. -pub(crate) fn for_impl_trait_as_generic( - ty: &ast::ImplTraitType, - existing_params: &ast::GenericParamList, -) -> SmolStr { - let c = ty - .type_bound_list() - .and_then(|bounds| bounds.syntax().text().char_at(0.into())) - .unwrap_or('T'); +impl NameGenerator { + /// Create a new empty generator + pub fn new() -> Self { + Self { pool: FxHashMap::default() } + } - for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) + /// Create a new generator with existing names. When suggesting a name, it will + /// avoid conflicts with existing names. + pub fn new_with_names<'a>(existing_names: impl Iterator) -> Self { + let mut generator = Self::new(); + existing_names.for_each(|name| generator.insert(name)); + generator + } + + /// Suggest a name without conflicts. If the name conflicts with existing names, + /// it will try to resolve the conflict by adding a numeric suffix. + pub fn suggest_name(&mut self, name: &str) -> SmolStr { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix.clone()) { + Entry::Vacant(entry) => { + entry.insert(suffix); + SmolStr::from_str(name).unwrap() + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count + 1).max(suffix); + + let mut new_name = SmolStrBuilder::new(); + new_name.push_str(&prefix); + new_name.push_str(count.to_string().as_str()); + new_name.finish() + } + } + } + + /// Suggest a name for given type. + /// + /// The function will strip references first, and suggest name from the inner type. + /// + /// - If `ty` is an ADT, it will suggest the name of the ADT. + /// + If `ty` is wrapped in `Box`, `Option` or `Result`, it will suggest the name from the inner type. + /// - If `ty` is a trait, it will suggest the name of the trait. + /// - If `ty` is an `impl Trait`, it will suggest the name of the first trait. + /// + /// If the suggested name conflicts with reserved keywords, it will return `None`. + pub fn for_type( + &mut self, + ty: &hir::Type, + db: &RootDatabase, + edition: Edition, + ) -> Option { + let name = name_of_type(ty, db, edition)?; + Some(self.suggest_name(&name)) + } + + /// Suggest name of impl trait type + /// + /// # Current implementation + /// + /// In current implementation, the function tries to get the name from the first + /// character of the name for the first type bound. + /// + /// If the name conflicts with existing generic parameters, it will try to + /// resolve the conflict with `for_unique_generic_name`. + pub fn for_impl_trait_as_generic(&mut self, ty: &ast::ImplTraitType) -> SmolStr { + let c = ty + .type_bound_list() + .and_then(|bounds| bounds.syntax().text().char_at(0.into())) + .unwrap_or('T'); + + self.suggest_name(&c.to_string()) + } + + /// Insert a name into the pool + fn insert(&mut self, name: &str) { + let (prefix, suffix) = Self::split_numeric_suffix(name); + let prefix = SmolStr::new(prefix); + let suffix = suffix.unwrap_or(0); + + match self.pool.entry(prefix) { + Entry::Vacant(entry) => { + entry.insert(suffix); + } + Entry::Occupied(mut entry) => { + let count = entry.get_mut(); + *count = (*count).max(suffix); + } + } + } + + /// Remove the numeric suffix from the name + /// + /// # Examples + /// `a1b2c3` -> `a1b2c` + fn split_numeric_suffix(name: &str) -> (&str, Option) { + let pos = + name.rfind(|c: char| !c.is_numeric()).expect("Name cannot be empty or all-numeric"); + let (prefix, suffix) = name.split_at(pos + 1); + (prefix, suffix.parse().ok()) + } } /// Suggest name of variable for given expression @@ -132,7 +210,7 @@ pub(crate) fn for_impl_trait_as_generic( /// /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. -pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { +pub fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost @@ -175,6 +253,10 @@ fn normalize(name: &str) -> Option { return None; } + if USELESS_NAME_PREFIXES.iter().any(|prefix| name.starts_with(prefix)) { + return None; + } + if !is_valid_name(&name) { return None; } @@ -184,7 +266,7 @@ fn normalize(name: &str) -> Option { fn is_valid_name(name: &str) -> bool { matches!( - ide_db::syntax_helpers::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), + super::LexedStr::single_token(syntax::Edition::CURRENT_FIXME, name), Some((syntax::SyntaxKind::IDENT, _error)) ) } @@ -818,4 +900,117 @@ fn foo(some_struct: S) { $0some_struct.some_field$0 } "some_field", ); } + + #[test] + fn from_and_to_func() { + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Bar::from(foo)$0); +} +"#, + "bar", + ); + + check( + r#" +//- minicore: from +struct Foo; +struct Bar; + +impl From for Bar { + fn from(_: Foo) -> Self { + Bar; + } +} + +fn f(_: Bar) {} + +fn main() { + let foo = Foo {}; + f($0Into::::into(foo)$0); +} +"#, + "bar", + ); + } + + #[test] + fn useless_name_prefix() { + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn from_foo(_: Foo) -> Self { + Foo {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::from_foo(foo)$0; +} +"#, + "bar", + ); + + check( + r#" +struct Foo; +struct Bar; + +impl Bar { + fn with_foo(_: Foo) -> Self { + Bar {} + } +} + +fn main() { + let foo = Foo {}; + let _ = $0Bar::with_foo(foo)$0; +} +"#, + "bar", + ); + } + + #[test] + fn conflicts_with_existing_names() { + let mut generator = NameGenerator::new(); + assert_eq!(generator.suggest_name("a"), "a"); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + assert_eq!(generator.suggest_name("a"), "a3"); + + assert_eq!(generator.suggest_name("b"), "b"); + assert_eq!(generator.suggest_name("b2"), "b2"); + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b"), "b4"); + assert_eq!(generator.suggest_name("b3"), "b5"); + + // --------- + let mut generator = NameGenerator::new_with_names(["a", "b", "b2", "c4"].into_iter()); + assert_eq!(generator.suggest_name("a"), "a1"); + assert_eq!(generator.suggest_name("a"), "a2"); + + assert_eq!(generator.suggest_name("b"), "b3"); + assert_eq!(generator.suggest_name("b2"), "b4"); + + assert_eq!(generator.suggest_name("c"), "c5"); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 18a95f0963dd..83a1eb44a616 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -369,6 +369,23 @@ mod F { ); } + #[test] + fn external_macro() { + check_diagnostics( + r#" +//- /library.rs library crate:library +#[macro_export] +macro_rules! trigger_lint { + () => { let FOO: () }; +} +//- /user.rs crate:user deps:library +fn foo() { + library::trigger_lint!(); +} + "#, + ); + } + #[test] fn complex_ignore() { check_diagnostics( @@ -418,6 +435,64 @@ fn f((_O): u8) {} ) } + #[test] + fn ignores_no_mangle_items() { + cov_mark::check!(extern_func_no_mangle_ignored); + check_diagnostics( + r#" +#[no_mangle] +extern "C" fn NonSnakeCaseName(some_var: u8) -> u8; + "#, + ); + } + + #[test] + fn ignores_no_mangle_items_with_no_abi() { + cov_mark::check!(extern_func_no_mangle_ignored); + check_diagnostics( + r#" +#[no_mangle] +extern fn NonSnakeCaseName(some_var: u8) -> u8; + "#, + ); + } + + #[test] + fn no_mangle_items_with_rust_abi() { + check_diagnostics( + r#" +#[no_mangle] +extern "Rust" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + + #[test] + fn no_mangle_items_non_extern() { + check_diagnostics( + r#" +#[no_mangle] +fn NonSnakeCaseName(some_var: u8) -> u8; +// ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + + #[test] + fn extern_fn_name() { + check_diagnostics( + r#" +extern "C" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +extern "Rust" fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` +extern fn NonSnakeCaseName(some_var: u8) -> u8; + // ^^^^^^^^^^^^^^^^ 💡 warn: Function `NonSnakeCaseName` should have snake_case name, e.g. `non_snake_case_name` + "#, + ); + } + #[test] fn ignores_extern_items() { cov_mark::check!(extern_func_incorrect_case_ignored); @@ -593,7 +668,7 @@ mod CheckBadStyle { } mod F { - //^ 💡 warn: Module `F` should have snake_case name, e.g. `f` + //^ 💡 error: Module `F` should have snake_case name, e.g. `f` #![deny(non_snake_case)] fn CheckItWorksWithModAttr() {} //^^^^^^^^^^^^^^^^^^^^^^^ 💡 error: Function `CheckItWorksWithModAttr` should have snake_case name, e.g. `check_it_works_with_mod_attr` @@ -856,4 +931,104 @@ fn func() { "#, ); } + + #[test] + fn override_lint_level() { + check_diagnostics( + r#" +#[warn(nonstandard_style)] +fn foo() { + let BAR; + // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` + #[allow(non_snake_case)] + let FOO; +} + +#[warn(nonstandard_style)] +fn foo() { + let BAR; + // ^^^ 💡 warn: Variable `BAR` should have snake_case name, e.g. `bar` + #[expect(non_snake_case)] + let FOO; + #[allow(non_snake_case)] + struct qux; + // ^^^ 💡 warn: Structure `qux` should have CamelCase name, e.g. `Qux` + + fn BAZ() { + // ^^^ 💡 error: Function `BAZ` should have snake_case name, e.g. `baz` + #![forbid(bad_style)] + } +} + "#, + ); + } + + #[test] + fn different_files() { + check_diagnostics( + r#" +//- /lib.rs +#![expect(nonstandard_style)] + +mod BAD_CASE; + +fn BAD_CASE() {} + +//- /BAD_CASE.rs +mod OtherBadCase; + // ^^^^^^^^^^^^ 💡 error: Module `OtherBadCase` should have snake_case name, e.g. `other_bad_case` + +//- /BAD_CASE/OtherBadCase.rs +#![deny(non_snake_case)] + +fn FOO() {} +// ^^^ 💡 error: Function `FOO` should have snake_case name, e.g. `foo` + +#[allow(bad_style)] +mod FINE_WITH_BAD_CASE; + +//- /BAD_CASE/OtherBadCase/FINE_WITH_BAD_CASE.rs +struct QUX; +const foo: i32 = 0; +fn BAR() { + let BAZ; +} + "#, + ); + } + + #[test] + fn cfged_lint_attrs() { + check_diagnostics( + r#" +//- /lib.rs cfg:feature=cool_feature +#[cfg_attr(any(), allow(non_snake_case))] +fn FOO() {} +// ^^^ 💡 warn: Function `FOO` should have snake_case name, e.g. `foo` + +#[cfg_attr(non_existent, allow(non_snake_case))] +fn BAR() {} +// ^^^ 💡 warn: Function `BAR` should have snake_case name, e.g. `bar` + +#[cfg_attr(feature = "cool_feature", allow(non_snake_case))] +fn BAZ() {} + +#[cfg_attr(feature = "cool_feature", cfg_attr ( all ( ) , allow ( non_snake_case ) ) ) ] +fn QUX() {} + "#, + ); + } + + #[test] + fn allow_with_comment() { + check_diagnostics( + r#" +#[allow( + // Yo, sup + non_snake_case +)] +fn foo(_HelloWorld: ()) {} + "#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs new file mode 100644 index 000000000000..ad4baf5e3a40 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs @@ -0,0 +1,1114 @@ +use hir::{CastError, ClosureStyle, HirDisplay}; + +use crate::{Diagnostic, DiagnosticCode, DiagnosticsContext}; + +macro_rules! format_ty { + ($ctx:expr, $fmt:literal, $($arg:expr),* $(,)?) => {{ + std::format!( + $fmt, + $( + $arg + .display($ctx.sema.db, $ctx.edition) + .with_closure_style(ClosureStyle::ClosureWithId) + ),* + ) + }} +} + +// Diagnostic: invalid-cast +// +// This diagnostic is triggered if the code contains an illegal cast +pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + let (code, message) = match d.error { + CastError::CastToBool => ( + DiagnosticCode::RustcHardError("E0054"), + format_ty!(ctx, "cannot cast `{}` as `bool`", d.expr_ty), + ), + CastError::CastToChar => ( + DiagnosticCode::RustcHardError("E0604"), + format_ty!(ctx, "only `u8` can be cast as `char`, not {}", d.expr_ty), + ), + CastError::DifferingKinds => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: vtable kinds may not match", + d.expr_ty, + d.cast_ty + ), + ), + CastError::SizedUnsizedCast => ( + DiagnosticCode::RustcHardError("E0607"), + format_ty!( + ctx, + "cannot cast thin pointer `{}` to fat pointer `{}`", + d.expr_ty, + d.cast_ty + ), + ), + CastError::Unknown | CastError::IllegalCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty), + ), + CastError::IntToFatCast => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty), + ), + CastError::NeedDeref => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs defererence or removal of unneeded borrow", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a raw pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaThinPtr => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through a thin pointer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NeedViaInt => ( + DiagnosticCode::RustcHardError("E0606"), + format_ty!( + ctx, + "casting `{}` as `{}` is invalid: needs casting through an integer first", + d.expr_ty, + d.cast_ty + ), + ), + CastError::NonScalar => ( + DiagnosticCode::RustcHardError("E0605"), + format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty), + ), + CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => ( + DiagnosticCode::RustcHardError("E0641"), + "cannot cast to a pointer of an unknown kind".to_owned(), + ), + }; + Diagnostic::new(code, message, display_range) +} + +// Diagnostic: cast-to-unsized +// +// This diagnostic is triggered when casting to an unsized type +pub(crate) fn cast_to_unsized(ctx: &DiagnosticsContext<'_>, d: &hir::CastToUnsized) -> Diagnostic { + let display_range = ctx.sema.diagnostics_display_range(d.expr.map(|it| it.into())); + Diagnostic::new( + DiagnosticCode::RustcHardError("E0620"), + format_ty!(ctx, "cast to unsized type: `{}`", d.cast_ty), + display_range, + ) +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_diagnostics, check_diagnostics_with_disabled}; + + #[test] + fn cast_as_bool() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let u = 5 as bool; + //^^^^^^^^^ error: cannot cast `i32` as `bool` + + let t = (1 + 2) as bool; + //^^^^^^^^^^^^^^^ error: cannot cast `i32` as `bool` + + let _ = 5_u32 as bool; + //^^^^^^^^^^^^^ error: cannot cast `u32` as `bool` + + let _ = 64.0_f64 as bool; + //^^^^^^^^^^^^^^^^ error: cannot cast `f64` as `bool` + + enum IntEnum { + Zero, + One, + Two + } + let _ = IntEnum::One as bool; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `IntEnum` as `bool` + + fn uwu(_: u8) -> i32 { + 5 + } + + unsafe fn owo() {} + + let _ = uwu as bool; + //^^^^^^^^^^^ error: cannot cast `fn uwu(u8) -> i32` as `bool` + let _ = owo as bool; + //^^^^^^^^^^^ error: cannot cast `unsafe fn owo()` as `bool` + + let _ = uwu as fn(u8) -> i32 as bool; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot cast `fn(u8) -> i32` as `bool` + let _ = 'x' as bool; + //^^^^^^^^^^^ error: cannot cast `char` as `bool` + + let ptr = 1 as *const (); + + let _ = ptr as bool; + //^^^^^^^^^^^ error: cannot cast `*const ()` as `bool` + let v = "hello" as bool; + //^^^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + ); + } + + #[test] + fn cast_pointee_projection() { + check_diagnostics( + r#" +//- minicore: sized +trait Tag<'a> { + type Type: ?Sized; +} + +trait IntoRaw: for<'a> Tag<'a> { + fn into_raw(this: *const >::Type) -> *mut >::Type; +} + +impl Tag<'a>> IntoRaw for T { + fn into_raw(this: *const >::Type) -> *mut >::Type { + this as *mut T::Type + } +} + +fn main() {} +"#, + ); + } + + #[test] + fn cast_region_to_int() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x: isize = 3; + let _ = &x as *const isize as usize; +} +"#, + ); + } + + #[test] + fn cast_to_bare_fn() { + check_diagnostics( + r#" +//- minicore: sized +fn foo(_x: isize) { } + +fn main() { + let v: u64 = 5; + let x = foo as extern "C" fn() -> isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `fn foo(isize)` as `fn() -> isize` + let y = v as extern "Rust" fn(isize) -> (isize, isize); + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `u64` as `fn(isize) -> (isize, isize)` + y(x()); +} +"#, + ); + } + + #[test] + fn cast_to_unit() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let _ = 0u32 as (); + //^^^^^^^^^^ error: non-primitive cast: `u32` as `()` +} +"#, + ); + } + + #[test] + fn cast_to_slice() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +fn as_bytes(_: &str) -> &[u8] { + loop {} +} + +fn main() { + as_bytes("example") as [char]; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cast to unsized type: `[char]` + + let arr: &[u8] = &[0, 2, 3]; + arr as [char]; + //^^^^^^^^^^^^^ error: cast to unsized type: `[char]` +} +"#, + &["E0308"], + ); + } + + #[test] + fn cast() { + check_diagnostics( + r#" +//- minicore: sized +fn null_mut() -> *mut T { + loop {} +} + +pub fn main() { + let i: isize = 'Q' as isize; + let _u: u32 = i as u32; + + // Test that `_` is correctly inferred. + let x = &"hello"; + let mut y = x as *const _; + y = null_mut(); +} +"#, + ); + } + + #[test] + fn dyn_tail_need_normalization() { + check_diagnostics( + r#" +//- minicore: dispatch_from_dyn +trait Trait { + type Associated; +} + +impl Trait for i32 { + type Associated = i64; +} + +trait Generic {} + +type TraitObject = dyn Generic<::Associated>; + +struct Wrap(TraitObject); + +fn cast(x: *mut TraitObject) { + x as *mut Wrap; +} +"#, + ); + } + + #[test] + fn enum_to_numeric_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub enum UnitOnly { + Foo, + Bar, + Baz, +} + +pub enum Fieldless { + Tuple(), + Struct{}, + Unit, +} + +pub enum NotUnitOnlyOrFieldless { + Foo, + Bar(u8), + Baz +} + +fn main() { + let unit_only = UnitOnly::Foo; + + let _ = unit_only as isize; + let _ = unit_only as i32; + let _ = unit_only as usize; + let _ = unit_only as u32; + + + let fieldless = Fieldless::Struct{}; + + let _ = fieldless as isize; + let _ = fieldless as i32; + let _ = fieldless as usize; + let _ = fieldless as u32; + + + let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo; + + let _ = not_unit_only_or_fieldless as isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize` + let _ = not_unit_only_or_fieldless as i32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32` + let _ = not_unit_only_or_fieldless as usize; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize` + let _ = not_unit_only_or_fieldless as u32; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32` +} +"#, + ); + } + + #[test] + fn fat_ptr_cast() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Foo { + fn foo(&self) {} //~ WARN method `foo` is never used +} + +struct Bar; + +impl Foo for Bar {} + +fn to_raw(_: *mut T) -> *mut () { + loop {} +} + +fn main() { + // Test we can turn a fat pointer to array back into a thin pointer. + let a: *const [i32] = &[1, 2, 3]; + let b = a as *const [i32; 2]; + + // Test conversion to an address (usize). + let a: *const [i32; 3] = &[1, 2, 3]; + let b: *const [i32] = a; + + // And conversion to a void pointer/address for trait objects too. + let a: *mut dyn Foo = &mut Bar; + let b = a as *mut () as usize; + let c = a as *const () as usize; + let d = to_raw(a) as usize; +} +"#, + &["E0308"], + ); + + check_diagnostics_with_disabled( + r#" +//- minicore: sized +trait Trait {} + +struct Box; + +impl Box { + fn new(_: T) -> Self { + loop {} + } +} + +fn as_ptr(_: &[i32]) -> *const i32 { + loop {} +} + +fn main() { + let a: &[i32] = &[1, 2, 3]; + let b: Box<[i32]> = Box::new([1, 2, 3]); + let p = a as *const [i32]; + let q = as_ptr(a); + + a as usize; + //^^^^^^^^^^ error: casting `&[i32]` as `usize` is invalid: needs casting through a raw pointer first + a as isize; + //^^^^^^^^^^ error: casting `&[i32]` as `isize` is invalid: needs casting through a raw pointer first + a as i16; + //^^^^^^^^ error: casting `&[i32]` as `i16` is invalid: needs casting through a raw pointer first + a as u32; + //^^^^^^^^ error: casting `&[i32]` as `u32` is invalid: needs casting through a raw pointer first + b as usize; + //^^^^^^^^^^ error: non-primitive cast: `Box<[i32]>` as `usize` + p as usize; + //^^^^^^^^^^ error: casting `*const [i32]` as `usize` is invalid: needs casting through a thin pointer first + q as *const [i32]; + //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]` + + let t: *mut (dyn Trait + 'static) = 0 as *mut _; + //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut _` + let mut fail: *const str = 0 as *const str; + //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str` + let mut fail2: *const str = 0isize as *const str; + //^^^^^^^^^^^^^^^^^^^^ error: cannot cast `isize` to a fat pointer `*const str` +} + +fn foo() { + let s = 0 as *const T; + //^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const T` +} +"#, + &["E0308", "unused_variables"], + ); + } + + #[test] + fn order_dependent_cast_inference() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let x = &"hello"; + let mut y = 0 as *const _; + //^^^^^^^^^^^^^ error: cannot cast to a pointer of an unknown kind + y = x as *const _; +} +"#, + ); + } + + #[test] + fn ptr_to_ptr_different_regions() { + check_diagnostics( + r#" +//- minicore: sized +struct Foo<'a> { a: &'a () } + +fn extend_lifetime_very_very_safely<'a>(v: *const Foo<'a>) -> *const Foo<'static> { + // This should pass because raw pointer casts can do anything they want. + v as *const Foo<'static> +} + +trait Trait {} + +fn assert_static<'a>(ptr: *mut (dyn Trait + 'a)) -> *mut (dyn Trait + 'static) { + ptr as _ +} + +fn main() { + let unit = (); + let foo = Foo { a: &unit }; + let _long: *const Foo<'static> = extend_lifetime_very_very_safely(&foo); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn add_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send) { + x as _ +} + +// (to test diagnostic list formatting) +fn add_multiple_auto<'a>(x: *mut dyn Trait<'a>) -> *mut (dyn Trait<'a> + Send + Sync + Unpin) { + x as _ +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_add_super_auto() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait: Send {} +impl Trait for () {} + +fn main() { + // This is OK: `Trait` has `Send` super trait. + &() as *const dyn Trait as *const (dyn Trait + Send); +} +"#, + ); + } + + #[test] + fn ptr_to_trait_obj_ok() { + check_diagnostics( + r#" +//- minicore: pointee +trait Trait<'a> {} + +fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> { + x as _ +} + +fn cast_inherent_lt<'a, 'b>(x: *mut (dyn Trait<'static> + 'a)) -> *mut (dyn Trait<'static> + 'b) { + x as _ +} + +fn unprincipled<'a, 'b>(x: *mut (dyn Send + 'a)) -> *mut (dyn Sync + 'b) { + x as _ +} +"#, + ); + } + + #[ignore = "issue #18047"] + #[test] + fn ptr_to_trait_obj_wrap_upcast() { + check_diagnostics( + r#" +//- minicore: sized +trait Super {} +trait Sub: Super {} + +struct Wrapper(T); + +// This cast should not compile. +// Upcasting can't work here, because we are also changing the type (`Wrapper`), +// and reinterpreting would be confusing/surprising. +// See +fn cast(ptr: *const dyn Sub) -> *const Wrapper { + ptr as _ + //^^^^^^^^ error: casting `*const dyn Sub` as `*const Wrapper` is invalid: vtable kinds may not match +} +"#, + ); + } + + #[test] + fn supported_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub fn main() { + struct String; + + let f = 1_usize as *const String; + + let _ = f as isize; + let _ = f as usize; + let _ = f as i8; + let _ = f as i16; + let _ = f as i32; + let _ = f as i64; + let _ = f as u8; + let _ = f as u16; + let _ = f as u32; + let _ = f as u64; + + let _ = 1 as isize; + let _ = 1 as usize; + let _ = 1 as *const String; + let _ = 1 as i8; + let _ = 1 as i16; + let _ = 1 as i32; + let _ = 1 as i64; + let _ = 1 as u8; + let _ = 1 as u16; + let _ = 1 as u32; + let _ = 1 as u64; + let _ = 1 as f32; + let _ = 1 as f64; + + let _ = 1_usize as isize; + let _ = 1_usize as usize; + let _ = 1_usize as *const String; + let _ = 1_usize as i8; + let _ = 1_usize as i16; + let _ = 1_usize as i32; + let _ = 1_usize as i64; + let _ = 1_usize as u8; + let _ = 1_usize as u16; + let _ = 1_usize as u32; + let _ = 1_usize as u64; + let _ = 1_usize as f32; + let _ = 1_usize as f64; + + let _ = 1i8 as isize; + let _ = 1i8 as usize; + let _ = 1i8 as *const String; + let _ = 1i8 as i8; + let _ = 1i8 as i16; + let _ = 1i8 as i32; + let _ = 1i8 as i64; + let _ = 1i8 as u8; + let _ = 1i8 as u16; + let _ = 1i8 as u32; + let _ = 1i8 as u64; + let _ = 1i8 as f32; + let _ = 1i8 as f64; + + let _ = 1u8 as isize; + let _ = 1u8 as usize; + let _ = 1u8 as *const String; + let _ = 1u8 as i8; + let _ = 1u8 as i16; + let _ = 1u8 as i32; + let _ = 1u8 as i64; + let _ = 1u8 as u8; + let _ = 1u8 as u16; + let _ = 1u8 as u32; + let _ = 1u8 as u64; + let _ = 1u8 as f32; + let _ = 1u8 as f64; + + let _ = 1i16 as isize; + let _ = 1i16 as usize; + let _ = 1i16 as *const String; + let _ = 1i16 as i8; + let _ = 1i16 as i16; + let _ = 1i16 as i32; + let _ = 1i16 as i64; + let _ = 1i16 as u8; + let _ = 1i16 as u16; + let _ = 1i16 as u32; + let _ = 1i16 as u64; + let _ = 1i16 as f32; + let _ = 1i16 as f64; + + let _ = 1u16 as isize; + let _ = 1u16 as usize; + let _ = 1u16 as *const String; + let _ = 1u16 as i8; + let _ = 1u16 as i16; + let _ = 1u16 as i32; + let _ = 1u16 as i64; + let _ = 1u16 as u8; + let _ = 1u16 as u16; + let _ = 1u16 as u32; + let _ = 1u16 as u64; + let _ = 1u16 as f32; + let _ = 1u16 as f64; + + let _ = 1i32 as isize; + let _ = 1i32 as usize; + let _ = 1i32 as *const String; + let _ = 1i32 as i8; + let _ = 1i32 as i16; + let _ = 1i32 as i32; + let _ = 1i32 as i64; + let _ = 1i32 as u8; + let _ = 1i32 as u16; + let _ = 1i32 as u32; + let _ = 1i32 as u64; + let _ = 1i32 as f32; + let _ = 1i32 as f64; + + let _ = 1u32 as isize; + let _ = 1u32 as usize; + let _ = 1u32 as *const String; + let _ = 1u32 as i8; + let _ = 1u32 as i16; + let _ = 1u32 as i32; + let _ = 1u32 as i64; + let _ = 1u32 as u8; + let _ = 1u32 as u16; + let _ = 1u32 as u32; + let _ = 1u32 as u64; + let _ = 1u32 as f32; + let _ = 1u32 as f64; + + let _ = 1i64 as isize; + let _ = 1i64 as usize; + let _ = 1i64 as *const String; + let _ = 1i64 as i8; + let _ = 1i64 as i16; + let _ = 1i64 as i32; + let _ = 1i64 as i64; + let _ = 1i64 as u8; + let _ = 1i64 as u16; + let _ = 1i64 as u32; + let _ = 1i64 as u64; + let _ = 1i64 as f32; + let _ = 1i64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = 1u64 as isize; + let _ = 1u64 as usize; + let _ = 1u64 as *const String; + let _ = 1u64 as i8; + let _ = 1u64 as i16; + let _ = 1u64 as i32; + let _ = 1u64 as i64; + let _ = 1u64 as u8; + let _ = 1u64 as u16; + let _ = 1u64 as u32; + let _ = 1u64 as u64; + let _ = 1u64 as f32; + let _ = 1u64 as f64; + + let _ = true as isize; + let _ = true as usize; + let _ = true as i8; + let _ = true as i16; + let _ = true as i32; + let _ = true as i64; + let _ = true as u8; + let _ = true as u16; + let _ = true as u32; + let _ = true as u64; + + let _ = 1f32 as isize; + let _ = 1f32 as usize; + let _ = 1f32 as i8; + let _ = 1f32 as i16; + let _ = 1f32 as i32; + let _ = 1f32 as i64; + let _ = 1f32 as u8; + let _ = 1f32 as u16; + let _ = 1f32 as u32; + let _ = 1f32 as u64; + let _ = 1f32 as f32; + let _ = 1f32 as f64; + + let _ = 1f64 as isize; + let _ = 1f64 as usize; + let _ = 1f64 as i8; + let _ = 1f64 as i16; + let _ = 1f64 as i32; + let _ = 1f64 as i64; + let _ = 1f64 as u8; + let _ = 1f64 as u16; + let _ = 1f64 as u32; + let _ = 1f64 as u64; + let _ = 1f64 as f32; + let _ = 1f64 as f64; +} +"#, + ); + } + + #[test] + fn unsized_struct_cast() { + check_diagnostics( + r#" +//- minicore: sized +pub struct Data([u8]); + +fn foo(x: &[u8]) { + let _: *const Data = x as *const Data; + //^^^^^^^^^^^^^^^^ error: casting `&[u8]` as `*const Data` is invalid +} +"#, + ); + } + + #[test] + fn unsupported_cast() { + check_diagnostics( + r#" +//- minicore: sized +struct A; + +fn main() { + let _ = 1.0 as *const A; + //^^^^^^^^^^^^^^^ error: casting `f64` as `*const A` is invalid +} +"#, + ); + } + + #[test] + fn issue_17897() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + _ = ((), ()) as (); + //^^^^^^^^^^^^^^ error: non-primitive cast: `((), ())` as `()` +} +"#, + ); + } + + #[test] + fn rustc_issue_10991() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let nil = (); + let _t = nil as usize; + //^^^^^^^^^^^^ error: non-primitive cast: `()` as `usize` +} +"#, + ); + } + + #[test] + fn rustc_issue_17444() { + check_diagnostics( + r#" +//- minicore: sized +enum Test { + Foo = 0 +} + +fn main() { + let _x = Test::Foo as *const isize; + //^^^^^^^^^^^^^^^^^^^^^^^^^ error: casting `Test` as `*const isize` is invalid +} +"#, + ); + } + + #[test] + fn rustc_issue_43825() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let error = error; + //^^^^^ error: no such value in this scope + + 0 as f32; + 0.0 as u32; +} +"#, + ); + } + + #[test] + fn rustc_issue_84213() { + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = something as *const Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*const Something` + + let _mut_pointer_to_something = something as *mut Something; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Something` as `*mut Something` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +struct Something { + pub field: u32, +} + +fn main() { + let mut something = Something { field: 1337 }; + let _ = something.field; + + let _pointer_to_something = &something as *const Something; + + let _mut_pointer_to_something = &mut something as *mut Something; +} +"#, + ); + } + + #[test] + fn rustc_issue_88621() { + check_diagnostics( + r#" +//- minicore: sized +#[repr(u8)] +enum Kind2 { + Foo() = 1, + Bar{} = 2, + Baz = 3, +} + +fn main() { + let _ = Kind2::Foo() as u8; + //^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `Kind2` as `u8` +} +"#, + ); + } + + #[test] + fn rustc_issue_89497() { + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { pointer as *const i32 as &'static i32 }; + //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: non-primitive cast: `*const i32` as `&i32` +} +"#, + ); + + // Fixed + check_diagnostics( + r#" +//- minicore: sized +fn main() { + let pointer: usize = &1_i32 as *const i32 as usize; + let _reference: &'static i32 = unsafe { &*(pointer as *const i32) }; +} +"#, + ); + } + + #[test] + fn rustc_issue_106883() { + check_diagnostics_with_disabled( + r#" +//- minicore: sized, deref +use core::ops::Deref; + +struct Foo; + +impl Deref for Foo { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &[] + } +} + +fn main() { + let _ = "foo" as bool; + //^^^^^^^^^^^^^ error: casting `&str` as `bool` is invalid: needs casting through a raw pointer first + + let _ = Foo as bool; + //^^^^^^^^^^^ error: non-primitive cast: `Foo` as `bool` +} + +fn _slice(bar: &[i32]) -> bool { + bar as bool + //^^^^^^^^^^^ error: casting `&[i32]` as `bool` is invalid: needs casting through a raw pointer first +} +"#, + &["E0308"], + ); + } + + #[test] + fn trait_upcasting() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +#![feature(trait_upcasting)] +trait Foo {} +trait Bar: Foo {} + +impl dyn Bar { + fn bar(&self) { + _ = self as &dyn Foo; + } +} +"#, + ); + } + + #[test] + fn issue_18047() { + check_diagnostics( + r#" +//- minicore: coerce_unsized, dispatch_from_dyn +trait LocalFrom { + fn from(_: T) -> Self; +} +trait LocalInto { + fn into(self) -> T; +} + +impl LocalInto for T +where + U: LocalFrom, +{ + fn into(self) -> U { + U::from(self) + } +} + +impl LocalFrom for T { + fn from(t: T) -> T { + t + } +} + +trait Foo { + type ErrorType; + type Assoc; +} + +trait Bar { + type ErrorType; +} + +struct ErrorLike; + +impl LocalFrom for ErrorLike +where + E: Trait + 'static, +{ + fn from(_: E) -> Self { + loop {} + } +} + +trait Baz { + type Assoc: Bar; + type Error: LocalInto; +} + +impl Baz for T +where + T: Foo, + T::ErrorType: LocalInto, + U: Bar, + ::ErrorType: LocalInto, +{ + type Assoc = U; + type Error = T::ErrorType; +} +struct S; +trait Trait {} +impl Trait for S {} + +fn test() +where + T: Baz, + T::Assoc: 'static, +{ + let _ = &S as &dyn Trait; +} +"#, + ); + } + + #[test] + fn cast_literal_to_char() { + check_diagnostics( + r#" +fn foo() { + 0 as char; +} + "#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index af8ac6005d7a..5b43f4b2af3d 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs @@ -11,9 +11,14 @@ use crate::{fix, Diagnostic, DiagnosticCode, DiagnosticsContext}; // // This diagnostic is triggered if an operation marked as `unsafe` is used outside of an `unsafe` function or block. pub(crate) fn missing_unsafe(ctx: &DiagnosticsContext<'_>, d: &hir::MissingUnsafe) -> Diagnostic { + let code = if d.only_lint { + DiagnosticCode::RustcLint("unsafe_op_in_unsafe_fn") + } else { + DiagnosticCode::RustcHardError("E0133") + }; Diagnostic::new_with_syntax_node_ptr( ctx, - DiagnosticCode::RustcHardError("E0133"), + code, "this operation is unsafe and requires an unsafe function or block", d.expr.map(|it| it.into()), ) @@ -99,8 +104,9 @@ mod tests { fn missing_unsafe_diagnostic_with_raw_ptr() { check_diagnostics( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; unsafe { let _y = *x; } let _z = *x; } //^^💡 error: this operation is unsafe and requires an unsafe function or block @@ -112,17 +118,18 @@ fn main() { fn missing_unsafe_diagnostic_with_unsafe_call() { check_diagnostics( r#" +//- minicore: sized struct HasUnsafe; impl HasUnsafe { unsafe fn unsafe_fn(&self) { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } } unsafe fn unsafe_fn() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _y = *x; } @@ -163,6 +170,56 @@ fn main() { ); } + #[test] + fn missing_unsafe_diagnostic_with_extern_static() { + check_diagnostics( + r#" +//- minicore: copy + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} + +fn main() { + let _x = EXTERN; + //^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + let _x = EXTERN_MUT; + //^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block + unsafe { + let _x = EXTERN; + let _x = EXTERN_MUT; + } +} +"#, + ); + } + + #[test] + fn no_unsafe_diagnostic_with_addr_of_static() { + check_diagnostics( + r#" +//- minicore: copy, addr_of + +use core::ptr::{addr_of, addr_of_mut}; + +extern "C" { + static EXTERN: i32; + static mut EXTERN_MUT: i32; +} +static mut STATIC_MUT: i32 = 0; + +fn main() { + let _x = addr_of!(EXTERN); + let _x = addr_of!(EXTERN_MUT); + let _x = addr_of!(STATIC_MUT); + let _x = addr_of_mut!(EXTERN_MUT); + let _x = addr_of_mut!(STATIC_MUT); +} +"#, + ); + } + #[test] fn no_missing_unsafe_diagnostic_with_safe_intrinsic() { check_diagnostics( @@ -200,14 +257,15 @@ fn main() { fn add_unsafe_block_when_dereferencing_a_raw_pointer() { check_fix( r#" +//- minicore: sized fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = *x$0; } "#, r#" fn main() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let _z = unsafe { *x }; } "#, @@ -218,8 +276,9 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_function() { check_fix( r#" +//- minicore: sized unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -228,7 +287,7 @@ fn main() { "#, r#" unsafe fn func() { - let x = &5 as *const usize; + let x = &5_usize as *const usize; let z = *x; } fn main() { @@ -242,6 +301,7 @@ fn main() { fn add_unsafe_block_when_calling_unsafe_method() { check_fix( r#" +//- minicore: sized struct S(usize); impl S { unsafe fn func(&self) { @@ -507,6 +567,30 @@ fn main() { ed2021::safe(); ed2024::not_safe(); //^^^^^^^^^^^^^^^^^^💡 error: this operation is unsafe and requires an unsafe function or block +} + "#, + ) + } + + #[test] + fn unsafe_op_in_unsafe_fn_allowed_by_default() { + check_diagnostics( + r#" +unsafe fn foo(p: *mut i32) { + *p = 123; +} + "#, + ) + } + + #[test] + fn unsafe_op_in_unsafe_fn() { + check_diagnostics( + r#" +#![warn(unsafe_op_in_unsafe_fn)] +unsafe fn foo(p: *mut i32) { + *p = 123; + //^^💡 warn: this operation is unsafe and requires an unsafe function or block } "#, ) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 06c6b0f3e4c3..a9ff06fb0ab1 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs @@ -190,4 +190,16 @@ fn foo(mut slice: &[u32]) -> usize { "#, ); } + + #[test] + fn regression_16564() { + check_diagnostics( + r#" +//- minicore: copy +fn test() { + let _x = (&(&mut (),)).0 as *const (); +} + "#, + ) + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e4b1f3ca9599..955427939150 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -824,13 +824,13 @@ fn f() { #[test] fn or_pattern() { + // FIXME: `None` is inferred as unknown here for some reason check_diagnostics( r#" //- minicore: option fn f(_: i32) {} fn main() { let ((Some(mut x), None) | (_, Some(mut x))) = (None, Some(7)) else { return }; - //^^^^^ 💡 warn: variable does not need to be mutable f(x); } "#, diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 87932bf989f0..18647206236c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs @@ -84,7 +84,7 @@ fn foo() { fn replace_filter_map_next_dont_work_for_not_sized_issues_16596() { check_diagnostics( r#" -//- minicore: iterators +//- minicore: iterators, dispatch_from_dyn fn foo() { let mut j = [0].into_iter(); let i: &mut dyn Iterator = &mut j; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs index b5c242e1e9f8..6994a7ed1465 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/typed_hole.rs @@ -402,4 +402,26 @@ fn f() { ], ); } + + #[test] + fn underscore_in_asm() { + check_diagnostics( + r#" +//- minicore: asm +fn rdtscp() -> u64 { + let hi: u64; + let lo: u64; + unsafe { + core::arch::asm!( + "rdtscp", + out("rdx") hi, + out("rax") lo, + out("rcx") _, + options(nomem, nostack, preserves_flags) + ); + } + (hi << 32) | lo +}"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs index 9b50a435e4c3..45c723d09d4c 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/lib.rs @@ -30,6 +30,7 @@ mod handlers { pub(crate) mod inactive_code; pub(crate) mod incoherent_impl; pub(crate) mod incorrect_case; + pub(crate) mod invalid_cast; pub(crate) mod invalid_derive_target; pub(crate) mod macro_error; pub(crate) mod malformed_derive; @@ -75,9 +76,10 @@ mod handlers { #[cfg(test)] mod tests; -use std::sync::LazyLock; +use std::{collections::hash_map, iter, sync::LazyLock}; -use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; +use either::Either; +use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::SourceDatabase, @@ -88,10 +90,10 @@ use ide_db::{ syntax_helpers::node_ext::parse_tt_as_comma_sep_paths, EditionedFileId, FileId, FileRange, FxHashMap, FxHashSet, RootDatabase, SnippetCap, }; -use stdx::never; +use itertools::Itertools; use syntax::{ - ast::{self, AstNode}, - AstPtr, Edition, SyntaxNode, SyntaxNodePtr, TextRange, + ast::{self, AstNode, HasAttrs}, + AstPtr, Edition, NodeOrToken, SmolStr, SyntaxKind, SyntaxNode, SyntaxNodePtr, TextRange, T, }; // FIXME: Make this an enum @@ -390,6 +392,7 @@ pub fn semantic_diagnostics( for diag in diags { let d = match diag { AnyDiagnostic::AwaitOutsideOfAsync(d) => handlers::await_outside_of_async::await_outside_of_async(&ctx, &d), + AnyDiagnostic::CastToUnsized(d) => handlers::invalid_cast::cast_to_unsized(&ctx, &d), AnyDiagnostic::ExpectedFunction(d) => handlers::expected_function::expected_function(&ctx, &d), AnyDiagnostic::InactiveCode(d) => match handlers::inactive_code::inactive_code(&ctx, &d) { Some(it) => it, @@ -397,6 +400,7 @@ pub fn semantic_diagnostics( } AnyDiagnostic::IncoherentImpl(d) => handlers::incoherent_impl::incoherent_impl(&ctx, &d), AnyDiagnostic::IncorrectCase(d) => handlers::incorrect_case::incorrect_case(&ctx, &d), + AnyDiagnostic::InvalidCast(d) => handlers::invalid_cast::invalid_cast(&ctx, &d), AnyDiagnostic::InvalidDeriveTarget(d) => handlers::invalid_derive_target::invalid_derive_target(&ctx, &d), AnyDiagnostic::MacroDefError(d) => handlers::macro_error::macro_def_error(&ctx, &d), AnyDiagnostic::MacroError(d) => handlers::macro_error::macro_error(&ctx, &d), @@ -473,8 +477,9 @@ pub fn semantic_diagnostics( || ctx.config.disable_experimental && d.experimental) }); - let mut diagnostics_of_range = res + let mut lints = res .iter_mut() + .filter(|it| matches!(it.code, DiagnosticCode::Clippy(_) | DiagnosticCode::RustcLint(_))) .filter_map(|it| { Some(( it.main_node.map(|ptr| { @@ -483,27 +488,31 @@ pub fn semantic_diagnostics( it, )) }) - .collect::>(); + .collect::>(); - if diagnostics_of_range.is_empty() { - return res; - } - - let mut rustc_stack: FxHashMap> = FxHashMap::default(); - let mut clippy_stack: FxHashMap> = FxHashMap::default(); - - // FIXME: This becomes quite expensive for big files - handle_lint_attributes( + // The edition isn't accurate (each diagnostics may have its own edition due to macros), + // but it's okay as it's only being used for error recovery. + handle_lints( &ctx.sema, - parse.syntax(), - &mut rustc_stack, - &mut clippy_stack, - &mut diagnostics_of_range, - ctx.edition, + &mut FxHashMap::default(), + &mut lints, + &mut Vec::new(), + file_id.edition(), ); res.retain(|d| d.severity != Severity::Allow); + res.retain_mut(|diag| { + if let Some(node) = diag + .main_node + .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) + { + handle_diag_from_macros(&ctx.sema, diag, &node) + } else { + true + } + }); + res } @@ -520,6 +529,35 @@ pub fn full_diagnostics( res } +/// Returns whether to keep this diagnostic (or remove it). +fn handle_diag_from_macros( + sema: &Semantics<'_, RootDatabase>, + diag: &mut Diagnostic, + node: &InFile, +) -> bool { + let Some(macro_file) = node.file_id.macro_file() else { return true }; + let span_map = sema.db.expansion_span_map(macro_file); + let mut spans = span_map.spans_for_range(node.text_range()); + if spans.any(|span| { + sema.db.lookup_intern_syntax_context(span.ctx).outer_expn.is_some_and(|expansion| { + let macro_call = + sema.db.lookup_intern_macro_call(expansion.as_macro_file().macro_call_id); + !Crate::from(macro_call.def.krate).origin(sema.db).is_local() + }) + }) { + // Disable suggestions for external macros, they'll change library code and it's just bad. + diag.fixes = None; + + // All Clippy lints report in macros, see https://github.com/rust-lang/rust-clippy/blob/903293b199364/declare_clippy_lint/src/lib.rs#L172. + if let DiagnosticCode::RustcLint(lint) = diag.code { + if !LINTS_TO_REPORT_IN_EXTERNAL_MACROS.contains(lint) { + return false; + } + }; + } + true +} + // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros static RUSTC_LINT_GROUPS_DICT: LazyLock>> = @@ -528,153 +566,347 @@ static RUSTC_LINT_GROUPS_DICT: LazyLock>> = static CLIPPY_LINT_GROUPS_DICT: LazyLock>> = LazyLock::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::")); +// FIXME: Autogenerate this instead of enumerating by hand. +static LINTS_TO_REPORT_IN_EXTERNAL_MACROS: LazyLock> = + LazyLock::new(|| FxHashSet::from_iter([])); + fn build_group_dict( lint_group: &'static [LintGroup], all_groups: &'static [&'static str], prefix: &'static str, ) -> FxHashMap<&'static str, Vec<&'static str>> { - let mut r: FxHashMap<&str, Vec<&str>> = FxHashMap::default(); + let mut map_with_prefixes: FxHashMap<&str, Vec<&str>> = FxHashMap::default(); for g in lint_group { - for child in g.children { - r.entry(child.strip_prefix(prefix).unwrap()) - .or_default() - .push(g.lint.label.strip_prefix(prefix).unwrap()); + let mut add_children = |label: &'static str| { + for child in g.children { + map_with_prefixes.entry(child).or_default().push(label); + } + }; + add_children(g.lint.label); + + if g.lint.label == "nonstandard_style" { + // Also add `bad_style`, which for some reason isn't listed in the groups. + add_children("bad_style"); } } - for (lint, groups) in r.iter_mut() { + for (lint, groups) in map_with_prefixes.iter_mut() { groups.push(lint); groups.extend_from_slice(all_groups); } - r + map_with_prefixes.into_iter().map(|(k, v)| (k.strip_prefix(prefix).unwrap(), v)).collect() } -fn handle_lint_attributes( +/// Thd default severity for lints that are not warn by default. +// FIXME: Autogenerate this instead of write manually. +static LINTS_DEFAULT_SEVERITY: LazyLock> = + LazyLock::new(|| FxHashMap::from_iter([("unsafe_op_in_unsafe_fn", Severity::Allow)])); + +fn handle_lints( sema: &Semantics<'_, RootDatabase>, - root: &SyntaxNode, - rustc_stack: &mut FxHashMap>, - clippy_stack: &mut FxHashMap>, - diagnostics_of_range: &mut FxHashMap, &mut Diagnostic>, + cache: &mut FxHashMap>, + diagnostics: &mut [(InFile, &mut Diagnostic)], + cache_stack: &mut Vec, edition: Edition, ) { - let _g = tracing::info_span!("handle_lint_attributes").entered(); - let file_id = sema.hir_file_for(root); - let preorder = root.preorder(); - for ev in preorder { - match ev { - syntax::WalkEvent::Enter(node) => { - for attr in node.children().filter_map(ast::Attr::cast) { - parse_lint_attribute( - attr, - rustc_stack, - clippy_stack, - |stack, severity| { - stack.push(severity); - }, - edition, - ); - } - if let Some(it) = - diagnostics_of_range.get_mut(&InFile { file_id, value: node.clone() }) - { - const EMPTY_LINTS: &[&str] = &[]; - let (names, stack) = match it.code { - DiagnosticCode::RustcLint(name) => ( - RUSTC_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), - &mut *rustc_stack, - ), - DiagnosticCode::Clippy(name) => ( - CLIPPY_LINT_GROUPS_DICT.get(name).map_or(EMPTY_LINTS, |it| &**it), - &mut *clippy_stack, - ), - _ => continue, - }; - for &name in names { - if let Some(s) = stack.get(name).and_then(|it| it.last()) { - it.severity = *s; - } - } - } - if let Some(item) = ast::Item::cast(node.clone()) { - if let Some(me) = sema.expand_attr_macro(&item) { - for stack in [&mut *rustc_stack, &mut *clippy_stack] { - stack - .entry("__RA_EVERY_LINT".to_owned()) - .or_default() - .push(Severity::Allow); - } - handle_lint_attributes( - sema, - &me, - rustc_stack, - clippy_stack, - diagnostics_of_range, - edition, - ); - for stack in [&mut *rustc_stack, &mut *clippy_stack] { - stack.entry("__RA_EVERY_LINT".to_owned()).or_default().pop(); - } - } - } - if let Some(mc) = ast::MacroCall::cast(node) { - if let Some(me) = sema.expand(&mc) { - handle_lint_attributes( - sema, - &me, - rustc_stack, - clippy_stack, - diagnostics_of_range, - edition, - ); - } - } - } - syntax::WalkEvent::Leave(node) => { - for attr in node.children().filter_map(ast::Attr::cast) { - parse_lint_attribute( - attr, - rustc_stack, - clippy_stack, - |stack, severity| { - if stack.pop() != Some(severity) { - never!("Mismatched serevity in walking lint attributes"); - } - }, - edition, - ); - } - } + for (node, diag) in diagnostics { + let lint = match diag.code { + DiagnosticCode::RustcLint(lint) | DiagnosticCode::Clippy(lint) => lint, + _ => panic!("non-lint passed to `handle_lints()`"), + }; + if let Some(&default_severity) = LINTS_DEFAULT_SEVERITY.get(lint) { + diag.severity = default_severity; + } + + let mut diag_severity = fill_lint_attrs(sema, node, cache, cache_stack, diag, edition); + + if let outline_diag_severity @ Some(_) = + find_outline_mod_lint_severity(sema, node, diag, edition) + { + diag_severity = outline_diag_severity; + } + + if let Some(diag_severity) = diag_severity { + diag.severity = diag_severity; } } } -fn parse_lint_attribute( - attr: ast::Attr, - rustc_stack: &mut FxHashMap>, - clippy_stack: &mut FxHashMap>, - job: impl Fn(&mut Vec, Severity), +fn find_outline_mod_lint_severity( + sema: &Semantics<'_, RootDatabase>, + node: &InFile, + diag: &Diagnostic, edition: Edition, -) { - let Some((tag, args_tt)) = attr.as_simple_call() else { - return; - }; - let severity = match tag.as_str() { - "allow" => Severity::Allow, - "warn" => Severity::Warning, - "forbid" | "deny" => Severity::Error, - _ => return, - }; - for lint in parse_tt_as_comma_sep_paths(args_tt, edition).into_iter().flatten() { - if let Some(lint) = lint.as_single_name_ref() { - job(rustc_stack.entry(lint.to_string()).or_default(), severity); +) -> Option { + let mod_node = node.value.ancestors().find_map(ast::Module::cast)?; + if mod_node.item_list().is_some() { + // Inline modules will be handled by `fill_lint_attrs()`. + return None; + } + + let mod_def = sema.to_module_def(&mod_node)?; + let module_source_file = sema.module_definition_node(mod_def); + let mut result = None; + let lint_groups = lint_groups(&diag.code); + lint_attrs( + sema, + ast::AnyHasAttrs::cast(module_source_file.value).expect("SourceFile always has attrs"), + edition, + ) + .for_each(|(lint, severity)| { + if lint_groups.contains(&&*lint) { + result = Some(severity); } - if let Some(tool) = lint.qualifier().and_then(|it| it.as_single_name_ref()) { - if let Some(name_ref) = &lint.segment().and_then(|it| it.name_ref()) { - if tool.to_string() == "clippy" { - job(clippy_stack.entry(name_ref.to_string()).or_default(), severity); + }); + result +} + +#[derive(Debug, Clone, Copy)] +struct SeverityAttr { + severity: Severity, + /// This field counts how far we are from the main node. Bigger values mean more far. + /// + /// Note this isn't accurate: there can be gaps between values (created when merging severity maps). + /// The important thing is that if an attr is closer to the main node, it will have smaller value. + /// + /// This is necessary even though we take care to never overwrite a value from deeper nesting + /// because of lint groups. For example, in the following code: + /// ``` + /// #[warn(non_snake_case)] + /// mod foo { + /// #[allow(nonstandard_style)] + /// mod bar; + /// } + /// ``` + /// We want to not warn on non snake case inside `bar`. If we are traversing this for the first + /// time, everything will be fine, because we will set `diag_severity` on the first matching group + /// and never overwrite it since then. But if `bar` is cached, the cache will contain both + /// `#[warn(non_snake_case)]` and `#[allow(nonstandard_style)]`, and without this field, we have + /// no way of differentiating between the two. + depth: u32, +} + +fn fill_lint_attrs( + sema: &Semantics<'_, RootDatabase>, + node: &InFile, + cache: &mut FxHashMap>, + cache_stack: &mut Vec, + diag: &Diagnostic, + edition: Edition, +) -> Option { + let mut collected_lint_attrs = FxHashMap::::default(); + let mut diag_severity = None; + + let mut ancestors = node.value.ancestors().peekable(); + let mut depth = 0; + loop { + let ancestor = ancestors.next().expect("we always return from top-level nodes"); + depth += 1; + + if ancestors.peek().is_none() { + // We don't want to insert too many nodes into cache, but top level nodes (aka. outline modules + // or macro expansions) need to touch the database so they seem like a good fit to cache. + + if let Some(cached) = cache.get_mut(&node.file_id) { + // This node (and everything above it) is already cached; the attribute is either here or nowhere. + + // Workaround for the borrow checker. + let cached = std::mem::take(cached); + + cached.iter().for_each(|(lint, severity)| { + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { + severity: severity.severity, + depth: severity.depth + depth, + }); + } + } + }); + + let all_matching_groups = lint_groups(&diag.code) + .iter() + .filter_map(|lint_group| cached.get(&**lint_group)); + let cached_severity = + all_matching_groups.min_by_key(|it| it.depth).map(|it| it.severity); + + cache.insert(node.file_id, cached); + + return diag_severity.or(cached_severity); + } + + // Insert this node's descendants' attributes into any outline descendant, but not including this node. + // This must come before inserting this node's own attributes to preserve order. + collected_lint_attrs.drain().for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity.severity); + } + + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(severity); + } + } + }); + + cache_stack.push(node.file_id); + cache.insert(node.file_id, FxHashMap::default()); + + if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { + // Insert this node's attributes into any outline descendant, including this node. + lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity); + } + + for item in &*cache_stack { + let node_cache_entry = cache + .get_mut(item) + .expect("we always insert cached nodes into the cache map"); + let lint_cache_entry = node_cache_entry.entry(lint.clone()); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { severity, depth }); + } + } + }); + } + + let parent_node = sema.find_parent_file(node.file_id); + if let Some(parent_node) = parent_node { + let parent_severity = + fill_lint_attrs(sema, &parent_node, cache, cache_stack, diag, edition); + if diag_severity.is_none() { + diag_severity = parent_severity; + } + } + cache_stack.pop(); + return diag_severity; + } else if let Some(ancestor) = ast::AnyHasAttrs::cast(ancestor) { + lint_attrs(sema, ancestor, edition).for_each(|(lint, severity)| { + if diag_severity.is_none() && lint_groups(&diag.code).contains(&&*lint) { + diag_severity = Some(severity); + } + + let lint_cache_entry = collected_lint_attrs.entry(lint); + if let hash_map::Entry::Vacant(lint_cache_entry) = lint_cache_entry { + // Do not overwrite existing lint attributes, as we go bottom to top and bottom attrs + // overwrite top attrs. + lint_cache_entry.insert(SeverityAttr { severity, depth }); + } + }); + } + } +} + +fn lint_attrs<'a>( + sema: &'a Semantics<'a, RootDatabase>, + ancestor: ast::AnyHasAttrs, + edition: Edition, +) -> impl Iterator + 'a { + ancestor + .attrs_including_inner() + .filter_map(|attr| { + attr.as_simple_call().and_then(|(name, value)| match &*name { + "allow" | "expect" => Some(Either::Left(iter::once((Severity::Allow, value)))), + "warn" => Some(Either::Left(iter::once((Severity::Warning, value)))), + "forbid" | "deny" => Some(Either::Left(iter::once((Severity::Error, value)))), + "cfg_attr" => { + let mut lint_attrs = Vec::new(); + cfg_attr_lint_attrs(sema, &value, &mut lint_attrs); + Some(Either::Right(lint_attrs.into_iter())) + } + _ => None, + }) + }) + .flatten() + .flat_map(move |(severity, lints)| { + parse_tt_as_comma_sep_paths(lints, edition).into_iter().flat_map(move |lints| { + // Rejoin the idents with `::`, so we have no spaces in between. + lints.into_iter().map(move |lint| { + ( + lint.segments().filter_map(|segment| segment.name_ref()).join("::").into(), + severity, + ) + }) + }) + }) +} + +fn cfg_attr_lint_attrs( + sema: &Semantics<'_, RootDatabase>, + value: &ast::TokenTree, + lint_attrs: &mut Vec<(Severity, ast::TokenTree)>, +) { + let prev_len = lint_attrs.len(); + + let mut iter = value.token_trees_and_tokens().filter(|it| match it { + NodeOrToken::Node(_) => true, + NodeOrToken::Token(it) => !it.kind().is_trivia(), + }); + + // Skip the condition. + for value in &mut iter { + if value.as_token().is_some_and(|it| it.kind() == T![,]) { + break; + } + } + + while let Some(value) = iter.next() { + if let Some(token) = value.as_token() { + if token.kind() == SyntaxKind::IDENT { + let severity = match token.text() { + "allow" | "expect" => Some(Severity::Allow), + "warn" => Some(Severity::Warning), + "forbid" | "deny" => Some(Severity::Error), + "cfg_attr" => { + if let Some(NodeOrToken::Node(value)) = iter.next() { + cfg_attr_lint_attrs(sema, &value, lint_attrs); + } + None + } + _ => None, + }; + if let Some(severity) = severity { + let lints = iter.next(); + if let Some(NodeOrToken::Node(lints)) = lints { + lint_attrs.push((severity, lints)); + } } } } } + + if prev_len != lint_attrs.len() { + if let Some(false) | None = sema.check_cfg_attr(value) { + // Discard the attributes when the condition is false. + lint_attrs.truncate(prev_len); + } + } +} + +fn lint_groups(lint: &DiagnosticCode) -> &'static [&'static str] { + match lint { + DiagnosticCode::RustcLint(name) => { + RUSTC_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default() + } + DiagnosticCode::Clippy(name) => { + CLIPPY_LINT_GROUPS_DICT.get(name).map(|it| &**it).unwrap_or_default() + } + _ => &[], + } } fn fix(id: &'static str, label: &str, source_change: SourceChange, target: TextRange) -> Assist { diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs index 925ae620231d..ea16a11d56d0 100644 --- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs +++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs @@ -219,7 +219,9 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::Local(_) | Definition::GenericParam(_) | Definition::Label(_) - | Definition::DeriveHelper(_) => None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => None, } .map(Definition::from) } @@ -672,7 +674,9 @@ fn filename_and_frag_for_def( | Definition::BuiltinAttr(_) | Definition::BuiltinLifetime(_) | Definition::ToolModule(_) - | Definition::DeriveHelper(_) => return None, + | Definition::DeriveHelper(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, }; Some((def, res, None)) diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index a939ed214ad8..79fdf75b7f7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -1,9 +1,10 @@ +use hir::db::ExpandDatabase; use hir::{InFile, MacroFileIdExt, Semantics}; +use ide_db::base_db::CrateId; use ide_db::{ - helpers::pick_best_token, syntax_helpers::insert_whitespace_into_node::insert_ws_into, FileId, - RootDatabase, + helpers::pick_best_token, syntax_helpers::prettify_macro_expansion, FileId, RootDatabase, }; -use span::Edition; +use span::{Edition, SpanMap, SyntaxContextId, TextRange, TextSize}; use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T}; use crate::FilePosition; @@ -27,6 +28,7 @@ pub struct ExpandedMacro { pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let file = sema.parse_guess_edition(position.file_id); + let krate = sema.file_to_module_def(position.file_id)?.krate().into(); let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind { SyntaxKind::IDENT => 1, @@ -61,8 +63,17 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .take_while(|it| it != &token) .filter(|it| it.kind() == T![,]) .count(); - let expansion = - format(db, SyntaxKind::MACRO_ITEMS, position.file_id, expansions.get(idx).cloned()?); + let expansion = expansions.get(idx)?.clone(); + let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?; + let expansion_span_map = db.expansion_span_map(expansion_file_id); + let expansion = format( + db, + SyntaxKind::MACRO_ITEMS, + position.file_id, + expansion, + &expansion_span_map, + krate, + ); Some(ExpandedMacro { name, expansion }) }); @@ -71,6 +82,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< } let mut anc = tok.parent_ancestors(); + let mut span_map = SpanMap::empty(); let (name, expanded, kind) = loop { let node = anc.next()?; @@ -85,7 +97,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< .unwrap_or(Edition::CURRENT), ) .to_string(), - expand_macro_recur(&sema, &item)?, + expand_macro_recur(&sema, &item, &mut span_map, TextSize::new(0))?, SyntaxKind::MACRO_ITEMS, ); } @@ -95,14 +107,23 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< name.push('!'); let syntax_kind = mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS); - break (name, expand_macro_recur(&sema, &ast::Item::MacroCall(mac))?, syntax_kind); + break ( + name, + expand_macro_recur( + &sema, + &ast::Item::MacroCall(mac), + &mut span_map, + TextSize::new(0), + )?, + syntax_kind, + ); } }; // FIXME: // macro expansion may lose all white space information // But we hope someday we can use ra_fmt for that - let expansion = format(db, kind, position.file_id, expanded); + let expansion = format(db, kind, position.file_id, expanded, &span_map, krate); Some(ExpandedMacro { name, expansion }) } @@ -110,6 +131,8 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< fn expand_macro_recur( sema: &Semantics<'_, RootDatabase>, macro_call: &ast::Item, + result_span_map: &mut SpanMap, + offset_in_original_node: TextSize, ) -> Option { let expanded = match macro_call { item @ ast::Item::MacroCall(macro_call) => sema @@ -118,29 +141,60 @@ fn expand_macro_recur( .clone_for_update(), item => sema.expand_attr_macro(item)?.clone_for_update(), }; - expand(sema, expanded) + let file_id = + sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file"); + let expansion_span_map = sema.db.expansion_span_map(file_id); + result_span_map.merge( + TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()), + expanded.text_range().len(), + &expansion_span_map, + ); + Some(expand(sema, expanded, result_span_map, u32::from(offset_in_original_node) as i32)) } -fn expand(sema: &Semantics<'_, RootDatabase>, expanded: SyntaxNode) -> Option { +fn expand( + sema: &Semantics<'_, RootDatabase>, + expanded: SyntaxNode, + result_span_map: &mut SpanMap, + mut offset_in_original_node: i32, +) -> SyntaxNode { let children = expanded.descendants().filter_map(ast::Item::cast); let mut replacements = Vec::new(); for child in children { - if let Some(new_node) = expand_macro_recur(sema, &child) { + if let Some(new_node) = expand_macro_recur( + sema, + &child, + result_span_map, + TextSize::new( + (offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32)) + as u32, + ), + ) { + offset_in_original_node = offset_in_original_node + + (u32::from(new_node.text_range().len()) as i32) + - (u32::from(child.syntax().text_range().len()) as i32); // check if the whole original syntax is replaced if expanded == *child.syntax() { - return Some(new_node); + return new_node; } replacements.push((child, new_node)); } } replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new)); - Some(expanded) + expanded } -fn format(db: &RootDatabase, kind: SyntaxKind, file_id: FileId, expanded: SyntaxNode) -> String { - let expansion = insert_ws_into(expanded).to_string(); +fn format( + db: &RootDatabase, + kind: SyntaxKind, + file_id: FileId, + expanded: SyntaxNode, + span_map: &SpanMap, + krate: CrateId, +) -> String { + let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string(); _format(db, kind, file_id, &expansion).unwrap_or(expansion) } @@ -498,7 +552,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -524,7 +578,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); } @@ -539,7 +593,7 @@ struct Foo {} "#, expect![[r#" Copy - impl < >$crate::marker::Copy for Foo< >where{}"#]], + impl < >core::marker::Copy for Foo< >where{}"#]], ); check( r#" @@ -550,7 +604,7 @@ struct Foo {} "#, expect![[r#" Clone - impl < >$crate::clone::Clone for Foo< >where { + impl < >core::clone::Clone for Foo< >where { fn clone(&self) -> Self { match self { Foo{} @@ -563,4 +617,44 @@ struct Foo {} }"#]], ); } + + #[test] + fn dollar_crate() { + check( + r#" +//- /a.rs crate:a +pub struct Foo; +#[macro_export] +macro_rules! m { + ( $i:ident ) => { $crate::Foo; $crate::Foo; $i::Foo; }; +} +//- /b.rs crate:b deps:a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { a::m!($crate); $crate::Foo; $crate::Foo; }; +} +//- /c.rs crate:c deps:b,a +pub struct Foo; +#[macro_export] +macro_rules! m { + () => { b::m!(); $crate::Foo; $crate::Foo; }; +} +fn bar() { + m$0!(); +} +"#, + expect![[r#" +m! +a::Foo; +a::Foo; +b::Foo; +; +b::Foo; +b::Foo; +; +crate::Foo; +crate::Foo;"#]], + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs index 971cd3ef585c..8836166d969d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs +++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs @@ -2750,4 +2750,36 @@ fn foo() { "#, ); } + + #[test] + fn issue_18138() { + check( + r#" +mod foo { + macro_rules! x { + () => { + pub struct Foo; + // ^^^ + }; + } + pub(crate) use x as m; +} + +mod bar { + use crate::m; + + m!(); + // ^^^^^ + + fn qux() { + Foo$0; + } +} + +mod m {} + +use foo::m; +"#, + ); + } } diff --git a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs index 5348e855be4b..4c8e3fc3040c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs +++ b/src/tools/rust-analyzer/crates/ide/src/highlight_related.rs @@ -2004,6 +2004,36 @@ fn main() { { return; } +"#, + ) + } + + #[test] + fn asm() { + check( + r#" +//- minicore: asm +#[inline] +pub unsafe fn bootstrap() -> ! { + builtin#asm( + "blabla", + "mrs {tmp}, CONTROL", + // ^^^ read + "blabla", + "bics {tmp}, {spsel}", + // ^^^ read + "blabla", + "msr CONTROL, {tmp}", + // ^^^ read + "blabla", + tmp$0 = inout(reg) 0, + // ^^^ + aaa = in(reg) 2, + aaa = in(reg) msp, + aaa = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} "#, ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs index 3e41b42be44b..83adf6548a89 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs @@ -3,8 +3,9 @@ use std::{mem, ops::Not}; use either::Either; use hir::{ - Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, HirDisplay, Layout, - LayoutError, Name, Semantics, Trait, Type, TypeInfo, + db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource, + HirDisplay, Layout, LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics, + Trait, Type, TypeInfo, }; use ide_db::{ base_db::SourceDatabase, @@ -12,7 +13,7 @@ use ide_db::{ documentation::HasDocs, famous_defs::FamousDefs, generated::lints::{CLIPPY_LINTS, DEFAULT_LINTS, FEATURES}, - syntax_helpers::insert_whitespace_into_node, + syntax_helpers::prettify_macro_expansion, RootDatabase, }; use itertools::Itertools; @@ -328,7 +329,7 @@ pub(super) fn try_for_lint(attr: &ast::Attr, token: &SyntaxToken) -> Option (false, FEATURES), - "allow" | "deny" | "forbid" | "warn" => { + "allow" | "deny" | "expect" | "forbid" | "warn" => { let is_clippy = algo::non_trivia_sibling(token.clone().into(), Direction::Prev) .filter(|t| t.kind() == T![:]) .and_then(|t| algo::non_trivia_sibling(t, Direction::Prev)) @@ -475,8 +476,9 @@ pub(super) fn definition( Err(_) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); - if source.file_id.is_macro() { - body = insert_whitespace_into_node::insert_ws_into(body); + if let Some(macro_file) = source.file_id.macro_file() { + let span_map = db.expansion_span_map(macro_file); + body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); } Some(body.to_string()) } @@ -485,8 +487,9 @@ pub(super) fn definition( Definition::Static(it) => { let source = it.source(db)?; let mut body = source.value.body()?.syntax().clone(); - if source.file_id.is_macro() { - body = insert_whitespace_into_node::insert_ws_into(body); + if let Some(macro_file) = source.file_id.macro_file() { + let span_map = db.expansion_span_map(macro_file); + body = prettify_macro_expansion(db, body, &span_map, it.krate(db).into()); } Some(body.to_string()) } @@ -526,6 +529,14 @@ pub(super) fn definition( _ => None, }; + let object_safety_info = if let Definition::Trait(it) = def { + let mut object_safety_info = String::new(); + render_object_safety(db, &mut object_safety_info, it.object_safety(db)); + Some(object_safety_info) + } else { + None + }; + let mut desc = String::new(); if let Some(notable_traits) = render_notable_trait_comment(db, notable_traits, edition) { desc.push_str(¬able_traits); @@ -535,6 +546,10 @@ pub(super) fn definition( desc.push_str(&layout_info); desc.push('\n'); } + if let Some(object_safety_info) = object_safety_info { + desc.push_str(&object_safety_info); + desc.push('\n'); + } desc.push_str(&label); if let Some(value) = value { desc.push_str(" = "); @@ -964,3 +979,62 @@ fn keyword_hints( _ => KeywordHint::new(token.text().to_owned(), format!("{}_keyword", token.text())), } } + +fn render_object_safety( + db: &RootDatabase, + buf: &mut String, + safety: Option, +) { + let Some(osv) = safety else { + buf.push_str("// Object Safety: Yes"); + return; + }; + buf.push_str("// Object Safety: No\n// - Reason: "); + match osv { + ObjectSafetyViolation::SizedSelf => { + buf.push_str("has a `Self: Sized` bound"); + } + ObjectSafetyViolation::SelfReferential => { + buf.push_str("has a bound that references `Self`"); + } + ObjectSafetyViolation::Method(func, mvc) => { + let name = hir::Function::from(func).name(db); + format_to!( + buf, + "has a method `{}` that is non dispatchable because of:\n// - ", + name.as_str() + ); + let desc = match mvc { + MethodViolationCode::StaticMethod => "missing a receiver", + MethodViolationCode::ReferencesSelfInput => "a parameter references `Self`", + MethodViolationCode::ReferencesSelfOutput => "the return type references `Self`", + MethodViolationCode::ReferencesImplTraitInTrait => { + "the return type contains `impl Trait`" + } + MethodViolationCode::AsyncFn => "being async", + MethodViolationCode::WhereClauseReferencesSelf => { + "a where clause references `Self`" + } + MethodViolationCode::Generic => "a non-lifetime generic parameter", + MethodViolationCode::UndispatchableReceiver => "a non-dispatchable receiver type", + }; + buf.push_str(desc); + } + ObjectSafetyViolation::AssocConst(const_) => { + let name = hir::Const::from(const_).name(db); + if let Some(name) = name { + format_to!(buf, "has an associated constant `{}`", name.as_str()); + } else { + buf.push_str("has an associated constant"); + } + } + ObjectSafetyViolation::GAT(alias) => { + let name = hir::TypeAlias::from(alias).name(db); + format_to!(buf, "has a generic associated type `{}`", name.as_str()); + } + ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => { + let name = hir::Trait::from(super_trait).name(db); + format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str()); + } + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs index 9585bdbe4c54..cca62d2181f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs @@ -182,13 +182,26 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); - let hover = analysis + let mut hover = analysis .hover( &HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions) } @@ -200,10 +213,23 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { fn check_hover_range_actions(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); - let hover = analysis + let mut hover = analysis .hover(&HoverConfig { links_in_hover: true, ..HOVER_BASE_CONFIG }, range) .unwrap() .unwrap(); + // stub out ranges into minicore as they can change every now and then + hover.info.actions.iter_mut().for_each(|action| match action { + super::HoverAction::GoToType(act) => act.iter_mut().for_each(|data| { + if data.nav.file_id == range.file_id { + return; + } + data.nav.full_range = TextRange::empty(span::TextSize::new(!0)); + if let Some(range) = &mut data.nav.focus_range { + *range = TextRange::empty(span::TextSize::new(!0)); + } + }), + _ => (), + }); expect.assert_debug_eq(&hover.info.actions); } @@ -483,8 +509,8 @@ fn main() { file_id: FileId( 1, ), - full_range: 632..867, - focus_range: 693..699, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "FnOnce", kind: Trait, container_name: "function", @@ -1470,6 +1496,24 @@ const foo$0: u32 = { ); } +#[test] +fn hover_unsigned_max_const() { + check( + r#"const $0A: u128 = -1_i128 as u128;"#, + expect![[r#" + *A* + + ```rust + test + ``` + + ```rust + const A: u128 = 340282366920938463463374607431768211455 (0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) + ``` + "#]], + ); +} + #[test] fn hover_eval_complex_constants() { check( @@ -3104,26 +3148,26 @@ struct S{ f1: u32 } fn main() { let s$0t = S{ f1:0 }; } "#, expect![[r#" - [ - GoToType( - [ - HoverGotoTypeData { - mod_path: "test::S", - nav: NavigationTarget { - file_id: FileId( - 0, - ), - full_range: 0..19, - focus_range: 7..8, - name: "S", - kind: Struct, - description: "struct S", - }, + [ + GoToType( + [ + HoverGotoTypeData { + mod_path: "test::S", + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 0..19, + focus_range: 7..8, + name: "S", + kind: Struct, + description: "struct S", }, - ], - ), - ] - "#]], + }, + ], + ), + ] + "#]], ); } @@ -3616,8 +3660,8 @@ pub mod future { file_id: FileId( 1, ), - full_range: 21..69, - focus_range: 60..66, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -5442,7 +5486,7 @@ const FOO$0: Option<&i32> = Some(2).as_ref(); fn hover_const_eval_dyn_trait() { check( r#" -//- minicore: fmt, coerce_unsized, builtin_impls +//- minicore: fmt, coerce_unsized, builtin_impls, dispatch_from_dyn use core::fmt::Debug; const FOO$0: &dyn Debug = &2i32; @@ -6292,7 +6336,19 @@ fn hover_lint() { arithmetic operation overflows "#]], - ) + ); + check( + r#"#![expect(arithmetic_overflow$0)]"#, + expect![[r#" + *arithmetic_overflow* + ``` + arithmetic_overflow + ``` + ___ + + arithmetic operation overflows + "#]], + ); } #[test] @@ -6308,7 +6364,19 @@ fn hover_clippy_lint() { Checks for `foo = bar; bar = foo` sequences. "#]], - ) + ); + check( + r#"#![expect(clippy::almost_swapped$0)]"#, + expect![[r#" + *almost_swapped* + ``` + clippy::almost_swapped + ``` + ___ + + Checks for `foo = bar; bar = foo` sequences. + "#]], + ); } #[test] @@ -7107,6 +7175,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7126,6 +7195,7 @@ impl T$0 for () {} ``` ```rust + // Object Safety: Yes trait T {} ``` "#]], @@ -7149,6 +7219,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { /* … */ } ``` "#]], @@ -7172,6 +7245,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7199,6 +7275,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -7226,6 +7305,9 @@ impl T$0 for () {} ``` ```rust + // Object Safety: No + // - Reason: has a method `func` that is non dispatchable because of: + // - missing a receiver trait T { fn func(); const FLAG: i32; @@ -8465,8 +8547,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 7800..8042, - focus_range: 7865..7871, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Future", kind: Trait, container_name: "future", @@ -8479,8 +8561,8 @@ impl Iterator for S { file_id: FileId( 1, ), - full_range: 8672..9171, - focus_range: 8749..8757, + full_range: 4294967295..4294967295, + focus_range: 4294967295..4294967295, name: "Iterator", kind: Trait, container_name: "iterator", @@ -8702,3 +8784,181 @@ fn foo() { "#]], ); } + +#[test] +fn test_hover_function_with_pat_param() { + check( + r#"fn test_1$0((start_range, end_range): (u32, u32), a: i32) {}"#, + expect![[r#" + *test_1* + + ```rust + test + ``` + + ```rust + fn test_1((start_range, end_range): (u32, u32), a: i32) + ``` + "#]], + ); + + // Test case with tuple pattern and mutable parameters + check( + r#"fn test_2$0((mut x, y): (i32, i32)) {}"#, + expect![[r#" + *test_2* + + ```rust + test + ``` + + ```rust + fn test_2((mut x, y): (i32, i32)) + ``` + "#]], + ); + + // Test case with a pattern in a reference type + check( + r#"fn test_3$0(&(a, b): &(i32, i32)) {}"#, + expect![[r#" + *test_3* + + ```rust + test + ``` + + ```rust + fn test_3(&(a, b): &(i32, i32)) + ``` + "#]], + ); + + // Test case with complex pattern (struct destructuring) + check( + r#"struct Point { x: i32, y: i32 } fn test_4$0(Point { x, y }: Point) {}"#, + expect![[r#" + *test_4* + + ```rust + test + ``` + + ```rust + fn test_4(Point { x, y }: Point) + ``` + "#]], + ); + + // Test case with a nested pattern + check( + r#"fn test_5$0(((a, b), c): ((i32, i32), i32)) {}"#, + expect![[r#" + *test_5* + + ```rust + test + ``` + + ```rust + fn test_5(((a, b), c): ((i32, i32), i32)) + ``` + "#]], + ); + + // Test case with an unused variable in the pattern + check( + r#"fn test_6$0((_, y): (i32, i64)) {}"#, + expect![[r#" + *test_6* + + ```rust + test + ``` + + ```rust + fn test_6((_, y): (i32, i64)) + ``` + "#]], + ); + + // Test case with a complex pattern involving both tuple and struct + check( + r#"struct Foo { a: i32, b: i32 } fn test_7$0((x, Foo { a, b }): (i32, Foo)) {}"#, + expect![[r#" + *test_7* + + ```rust + test + ``` + + ```rust + fn test_7((x, Foo { a, b }): (i32, Foo)) + ``` + "#]], + ); + + // Test case with Enum and Or pattern + check( + r#"enum MyEnum { A(i32), B(i32) } fn test_8$0((MyEnum::A(x) | MyEnum::B(x)): MyEnum) {}"#, + expect![[r#" + *test_8* + + ```rust + test + ``` + + ```rust + fn test_8((MyEnum::A(x) | MyEnum::B(x)): MyEnum) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter + check( + r#"struct Foo { a: i32, b: i32 } fn test_9$0(Foo { a, b }: Foo) {}"#, + expect![[r#" + *test_9* + + ```rust + test + ``` + + ```rust + fn test_9(Foo { a, b }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with a different name + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: b1 }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: b1 }: Foo) + ``` + "#]], + ); + + // Test case with a pattern as a function parameter with annotations + check( + r#"struct Foo { a: i32, b: i32 } fn test_10$0(Foo { a, b: mut b }: Foo) {}"#, + expect![[r#" + *test_10* + + ```rust + test + ``` + + ```rust + fn test_10(Foo { a, b: mut b }: Foo) + ``` + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs index 6a5d5e26a4f0..97e712356b54 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs @@ -14,8 +14,8 @@ use smallvec::{smallvec, SmallVec}; use span::{Edition, EditionedFileId}; use stdx::never; use syntax::{ - ast::{self, AstNode}, - match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, + ast::{self, AstNode, HasGenericParams}, + format_smolstr, match_ast, SmolStr, SyntaxNode, TextRange, TextSize, WalkEvent, }; use text_edit::TextEdit; @@ -29,13 +29,230 @@ mod closing_brace; mod closure_captures; mod closure_ret; mod discriminant; -mod fn_lifetime_fn; mod generic_param; mod implicit_drop; mod implicit_static; +mod lifetime; mod param_name; mod range_exclusive; +// Feature: Inlay Hints +// +// rust-analyzer shows additional information inline with the source code. +// Editors usually render this using read-only virtual text snippets interspersed with code. +// +// rust-analyzer by default shows hints for +// +// * types of local variables +// * names of function arguments +// * names of const generic parameters +// * types of chained expressions +// +// Optionally, one can enable additional hints for +// +// * return types of closure expressions +// * elided lifetimes +// * compiler inserted reborrows +// * names of generic type and lifetime parameters +// +// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if +// any of the +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] +// are met: +// +// * the parameter name is a suffix of the function's name +// * the argument is a qualified constructing or call expression where the qualifier is an ADT +// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix +// of argument with _ splitting it off +// * the parameter name starts with `ra_fixture` +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] +// in a unary function +// * the parameter name is a +// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] +// in a unary function +// +// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] +pub(crate) fn inlay_hints( + db: &RootDatabase, + file_id: FileId, + range_limit: Option, + config: &InlayHintsConfig, +) -> Vec { + let _p = tracing::info_span!("inlay_hints").entered(); + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let file = file.syntax(); + + let mut acc = Vec::new(); + + let Some(scope) = sema.scope(file) else { + return acc; + }; + let famous_defs = FamousDefs(&sema, scope.krate()); + + let ctx = &mut InlayHintCtx::default(); + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } + }; + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!((&event, range_limit), (WalkEvent::Enter(node), Some(range)) if range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } + acc +} + +#[derive(Default)] +struct InlayHintCtx { + lifetime_stacks: Vec>, +} + +pub(crate) fn inlay_hints_resolve( + db: &RootDatabase, + file_id: FileId, + resolve_range: TextRange, + hash: u64, + config: &InlayHintsConfig, + hasher: impl Fn(&InlayHint) -> u64, +) -> Option { + let _p = tracing::info_span!("inlay_hints_resolve").entered(); + let sema = Semantics::new(db); + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let file = file.syntax(); + + let scope = sema.scope(file)?; + let famous_defs = FamousDefs(&sema, scope.krate()); + let mut acc = Vec::new(); + + let ctx = &mut InlayHintCtx::default(); + let mut hints = |event| { + if let Some(node) = handle_event(ctx, event) { + hints(&mut acc, ctx, &famous_defs, config, file_id, node); + } + }; + + let mut preorder = file.preorder(); + while let Some(event) = preorder.next() { + // FIXME: This can miss some hints that require the parent of the range to calculate + if matches!(&event, WalkEvent::Enter(node) if resolve_range.intersect(node.text_range()).is_none()) + { + preorder.skip_subtree(); + continue; + } + hints(event); + } + acc.into_iter().find(|hint| hasher(hint) == hash) +} + +fn handle_event(ctx: &mut InlayHintCtx, node: WalkEvent) -> Option { + match node { + WalkEvent::Enter(node) => { + if let Some(node) = ast::AnyHasGenericParams::cast(node.clone()) { + let params = node + .generic_param_list() + .map(|it| { + it.lifetime_params() + .filter_map(|it| { + it.lifetime().map(|it| format_smolstr!("{}", &it.text()[1..])) + }) + .collect() + }) + .unwrap_or_default(); + ctx.lifetime_stacks.push(params); + } + Some(node) + } + WalkEvent::Leave(n) => { + if ast::AnyHasGenericParams::can_cast(n.kind()) { + ctx.lifetime_stacks.pop(); + } + None + } + } +} + +// FIXME: At some point when our hir infra is fleshed out enough we should flip this and traverse the +// HIR instead of the syntax tree. +fn hints( + hints: &mut Vec, + ctx: &mut InlayHintCtx, + famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + node: SyntaxNode, +) { + closing_brace::hints(hints, sema, config, file_id, node.clone()); + if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { + generic_param::hints(hints, sema, config, any_has_generic_args); + } + + match_ast! { + match node { + ast::Expr(expr) => { + chaining::hints(hints, famous_defs, config, file_id, &expr); + adjustment::hints(hints, famous_defs, config, file_id, &expr); + match expr { + ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)), + ast::Expr::MethodCallExpr(it) => { + param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)) + } + ast::Expr::ClosureExpr(it) => { + closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); + closure_ret::hints(hints, famous_defs, config, file_id, it) + }, + ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it), + _ => Some(()), + } + }, + ast::Pat(it) => { + binding_mode::hints(hints, famous_defs, config, file_id, &it); + match it { + ast::Pat::IdentPat(it) => { + bind_pat::hints(hints, famous_defs, config, file_id, &it); + } + ast::Pat::RangePat(it) => { + range_exclusive::hints(hints, famous_defs, config, file_id, it); + } + _ => {} + } + Some(()) + }, + ast::Item(it) => match it { + ast::Item::Fn(it) => { + implicit_drop::hints(hints, famous_defs, config, file_id, &it); + lifetime::fn_hints(hints, ctx, famous_defs, config, file_id, it) + }, + // static type elisions + ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)), + ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)), + ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), + _ => None, + }, + // FIXME: trait object type elisions + ast::Type(ty) => match ty { + ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config, file_id, ptr), + ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path), + _ => Some(()), + }, + _ => Some(()), + } + }; +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct InlayHintsConfig { pub render_colons: bool, @@ -162,6 +379,9 @@ pub struct InlayHint { pub label: InlayHintLabel, /// Text edit to apply when "accepting" this inlay hint. pub text_edit: Option, + /// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the + /// hint does not support resolving. + pub resolve_parent: Option, } impl std::hash::Hash for InlayHint { @@ -186,6 +406,7 @@ impl InlayHint { position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, } } @@ -198,11 +419,12 @@ impl InlayHint { position: InlayHintPosition::Before, pad_left: false, pad_right: false, + resolve_parent: None, } } - pub fn needs_resolve(&self) -> bool { - self.text_edit.is_some() || self.label.needs_resolve() + pub fn needs_resolve(&self) -> Option { + self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve()) } } @@ -355,11 +577,14 @@ impl HirWrite for InlayHintLabelBuilder<'_> { impl InlayHintLabelBuilder<'_> { fn make_new_part(&mut self) { - self.result.parts.push(InlayHintLabelPart { - text: take(&mut self.last_part), - linked_location: self.location.take(), - tooltip: None, - }); + let text = take(&mut self.last_part); + if !text.is_empty() { + self.result.parts.push(InlayHintLabelPart { + text, + linked_location: self.location.take(), + tooltip: None, + }); + } } fn finish(mut self) -> InlayHintLabel { @@ -434,190 +659,6 @@ fn label_of_ty( Some(r) } -fn ty_to_text_edit( - sema: &Semantics<'_, RootDatabase>, - node_for_hint: &SyntaxNode, - ty: &hir::Type, - offset_to_insert: TextSize, - prefix: String, -) -> Option { - let scope = sema.scope(node_for_hint)?; - // FIXME: Limit the length and bail out on excess somehow? - let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; - - let mut builder = TextEdit::builder(); - builder.insert(offset_to_insert, prefix); - builder.insert(offset_to_insert, rendered); - Some(builder.finish()) -} - -// Feature: Inlay Hints -// -// rust-analyzer shows additional information inline with the source code. -// Editors usually render this using read-only virtual text snippets interspersed with code. -// -// rust-analyzer by default shows hints for -// -// * types of local variables -// * names of function arguments -// * names of const generic parameters -// * types of chained expressions -// -// Optionally, one can enable additional hints for -// -// * return types of closure expressions -// * elided lifetimes -// * compiler inserted reborrows -// * names of generic type and lifetime parameters -// -// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if -// any of the -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria] -// are met: -// -// * the parameter name is a suffix of the function's name -// * the argument is a qualified constructing or call expression where the qualifier is an ADT -// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix -// of argument with _ splitting it off -// * the parameter name starts with `ra_fixture` -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name] -// in a unary function -// * the parameter name is a -// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character] -// in a unary function -// -// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] -pub(crate) fn inlay_hints( - db: &RootDatabase, - file_id: FileId, - range_limit: Option, - config: &InlayHintsConfig, -) -> Vec { - let _p = tracing::info_span!("inlay_hints").entered(); - let sema = Semantics::new(db); - let file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); - let file = sema.parse(file_id); - let file = file.syntax(); - - let mut acc = Vec::new(); - - if let Some(scope) = sema.scope(file) { - let famous_defs = FamousDefs(&sema, scope.krate()); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - match range_limit { - Some(range) => match file.covering_element(range) { - NodeOrToken::Token(_) => return acc, - NodeOrToken::Node(n) => n - .descendants() - .filter(|descendant| range.intersect(descendant.text_range()).is_some()) - .for_each(hints), - }, - None => file.descendants().for_each(hints), - }; - } - - acc -} - -pub(crate) fn inlay_hints_resolve( - db: &RootDatabase, - file_id: FileId, - position: TextSize, - hash: u64, - config: &InlayHintsConfig, - hasher: impl Fn(&InlayHint) -> u64, -) -> Option { - let _p = tracing::info_span!("inlay_hints_resolve").entered(); - let sema = Semantics::new(db); - let file_id = sema - .attach_first_edition(file_id) - .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); - let file = sema.parse(file_id); - let file = file.syntax(); - - let scope = sema.scope(file)?; - let famous_defs = FamousDefs(&sema, scope.krate()); - let mut acc = Vec::new(); - - let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); - let token = file.token_at_offset(position).left_biased()?; - if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) { - parent_block.syntax().descendants().for_each(hints) - } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) { - parent_item.syntax().descendants().for_each(hints) - } else { - return None; - } - - acc.into_iter().find(|hint| hasher(hint) == hash) -} - -fn hints( - hints: &mut Vec, - famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>, - config: &InlayHintsConfig, - file_id: EditionedFileId, - node: SyntaxNode, -) { - closing_brace::hints(hints, sema, config, file_id, node.clone()); - if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) { - generic_param::hints(hints, sema, config, any_has_generic_args); - } - match_ast! { - match node { - ast::Expr(expr) => { - chaining::hints(hints, famous_defs, config, file_id, &expr); - adjustment::hints(hints, sema, config, file_id, &expr); - match expr { - ast::Expr::CallExpr(it) => param_name::hints(hints, sema, config, ast::Expr::from(it)), - ast::Expr::MethodCallExpr(it) => { - param_name::hints(hints, sema, config, ast::Expr::from(it)) - } - ast::Expr::ClosureExpr(it) => { - closure_captures::hints(hints, famous_defs, config, file_id, it.clone()); - closure_ret::hints(hints, famous_defs, config, file_id, it) - }, - ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it), - _ => None, - } - }, - ast::Pat(it) => { - binding_mode::hints(hints, sema, config, &it); - match it { - ast::Pat::IdentPat(it) => { - bind_pat::hints(hints, famous_defs, config, file_id, &it); - } - ast::Pat::RangePat(it) => { - range_exclusive::hints(hints, config, it); - } - _ => {} - } - Some(()) - }, - ast::Item(it) => match it { - // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints - ast::Item::Impl(_) => None, - ast::Item::Fn(it) => { - implicit_drop::hints(hints, sema, config, file_id, &it); - fn_lifetime_fn::hints(hints, config, it) - }, - // static type elisions - ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)), - ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), - ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it), - _ => None, - }, - // FIXME: fn-ptr type, dyn fn type, and trait object type elisions - ast::Type(_) => None, - _ => None, - } - }; -} - /// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator. fn hint_iterator( sema: &Semantics<'_, RootDatabase>, @@ -653,6 +694,23 @@ fn hint_iterator( None } +fn ty_to_text_edit( + sema: &Semantics<'_, RootDatabase>, + node_for_hint: &SyntaxNode, + ty: &hir::Type, + offset_to_insert: TextSize, + prefix: String, +) -> Option { + let scope = sema.scope(node_for_hint)?; + // FIXME: Limit the length and bail out on excess somehow? + let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?; + + let mut builder = TextEdit::builder(); + builder.insert(offset_to_insert, prefix); + builder.insert(offset_to_insert, rendered); + Some(builder.finish()) +} + fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool { matches!(closure.body(), Some(ast::Expr::BlockExpr(_))) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs index 756198d0c011..c37c469dff4e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs @@ -6,9 +6,8 @@ use either::Either; use hir::{ Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety, - Semantics, }; -use ide_db::RootDatabase; +use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; use stdx::never; @@ -24,7 +23,7 @@ use crate::{ pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, expr: &ast::Expr, @@ -156,6 +155,7 @@ pub(super) fn hints( kind: InlayKind::Adjustment, label, text_edit: None, + resolve_parent: Some(expr.syntax().text_range()), }); } if !postfix && needs_inner_parens { @@ -288,7 +288,7 @@ mod tests { check_with_config( InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { let _: u32 = loop {}; //^^^^^^^ @@ -313,6 +313,7 @@ fn main() { //^^^^^^^^^^^^ //^^^^^^^^^^^^( //^^^^^^^^^^^^) + //^^^^ let _: fn() = || {}; //^^^^^ let _: unsafe fn() = || {}; @@ -321,6 +322,8 @@ fn main() { //^^^^^^^^^^^^^^^^^^^^^ //^^^^^^^^^^^^^^^^^^^^^( //^^^^^^^^^^^^^^^^^^^^^) + //^^^^^^^^^&raw mut $ + //^^^^^^^^^* let _: &mut [_] = &mut [0; 0]; //^^^^^^^^^^^ //^^^^^^^^^^^&mut $ @@ -428,7 +431,7 @@ impl core::ops::IndexMut for Struct {} ..DISABLED_CONFIG }, r#" -//- minicore: coerce_unsized, fn, eq, index +//- minicore: coerce_unsized, fn, eq, index, dispatch_from_dyn fn main() { Struct.consume(); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs index 82b0a6ffcf13..7a808fb4a929 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bind_pat.rs @@ -110,6 +110,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: !render_colons, pad_right: false, + resolve_parent: Some(pat.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs index f27390ee898b..d1c0677863db 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/binding_mode.rs @@ -2,17 +2,19 @@ //! ```no_run //! let /* & */ (/* ref */ x,) = &(0,); //! ``` -use hir::{Mutability, Semantics}; -use ide_db::RootDatabase; +use hir::Mutability; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::ast::{self, AstNode}; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, pat: &ast::Pat, ) -> Option<()> { if !config.binding_mode_hints { @@ -57,6 +59,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: mut_reference, + resolve_parent: Some(pat.syntax().text_range()), }); }); match pat { @@ -75,6 +78,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(pat.syntax().text_range()), }); } ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => { diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs index 35f4d46e187c..58d8f97a8ced 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs @@ -67,6 +67,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(expr.syntax().text_range()), }); } } @@ -139,7 +140,6 @@ fn main() { ( 147..172, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -152,13 +152,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 147..154, [ - "", InlayHintLabelPart { text: "A", linked_location: Some( @@ -171,7 +169,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -222,7 +219,6 @@ fn main() { ( 143..190, [ - "", InlayHintLabelPart { text: "C", linked_location: Some( @@ -235,13 +231,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 143..179, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -254,7 +248,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -289,7 +282,6 @@ fn main() { ( 143..190, [ - "", InlayHintLabelPart { text: "C", linked_location: Some( @@ -302,13 +294,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 143..179, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -321,7 +311,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -357,7 +346,6 @@ fn main() { ( 246..283, [ - "", InlayHintLabelPart { text: "B", linked_location: Some( @@ -389,7 +377,6 @@ fn main() { ( 246..265, [ - "", InlayHintLabelPart { text: "A", linked_location: Some( @@ -562,7 +549,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ] @@ -597,7 +583,6 @@ fn main() { ( 124..130, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -610,13 +595,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 145..185, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -629,13 +612,11 @@ fn main() { ), tooltip: "", }, - "", ], ), ( 145..168, [ - "", InlayHintLabelPart { text: "Struct", linked_location: Some( @@ -648,7 +629,6 @@ fn main() { ), tooltip: "", }, - "", ], ), ( diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs index d78fd64bdf4d..90b8be64a46d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs @@ -18,12 +18,13 @@ pub(super) fn hints( sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, file_id: EditionedFileId, - mut node: SyntaxNode, + original_node: SyntaxNode, ) -> Option<()> { let min_lines = config.closing_brace_hints_min_lines?; let name = |it: ast::Name| it.syntax().text_range(); + let mut node = original_node.clone(); let mut closing_token; let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) { closing_token = item_list.r_curly_token()?; @@ -77,7 +78,7 @@ pub(super) fn hints( } closing_token = block.r_curly_token()?; - let lifetime = label.lifetime().map_or_else(String::new, |it| it.to_string()); + let lifetime = label.lifetime()?.to_string(); (lifetime, Some(label.syntax().text_range())) } else if let Some(block) = ast::BlockExpr::cast(node.clone()) { @@ -145,6 +146,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: true, pad_right: false, + resolve_parent: Some(original_node.text_range()), }); None diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs index e87e10d8504e..f399bd01d071 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_captures.rs @@ -3,7 +3,7 @@ //! Tests live in [`bind_pat`][super::bind_pat] module. use ide_db::famous_defs::FamousDefs; use span::EditionedFileId; -use stdx::TupleExt; +use stdx::{never, TupleExt}; use syntax::ast::{self, AstNode}; use text_edit::{TextRange, TextSize}; @@ -40,6 +40,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); range } @@ -52,6 +53,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); let last = captures.len() - 1; for (idx, capture) in captures.into_iter().enumerate() { @@ -61,17 +63,21 @@ pub(super) fn hints( // force cache the source file, otherwise sema lookup will potentially panic _ = sema.parse_or_expand(source.file()); + let label = format!( + "{}{}", + match capture.kind() { + hir::CaptureKind::SharedRef => "&", + hir::CaptureKind::UniqueSharedRef => "&unique ", + hir::CaptureKind::MutableRef => "&mut ", + hir::CaptureKind::Move => "", + }, + capture.display_place(sema.db) + ); + if never!(label.is_empty()) { + continue; + } let label = InlayHintLabel::simple( - format!( - "{}{}", - match capture.kind() { - hir::CaptureKind::SharedRef => "&", - hir::CaptureKind::UniqueSharedRef => "&unique ", - hir::CaptureKind::MutableRef => "&mut ", - hir::CaptureKind::Move => "", - }, - capture.display_place(sema.db) - ), + label, None, source.name().and_then(|name| { name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into) @@ -85,6 +91,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); if idx != last { @@ -96,6 +103,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: None, }); } } @@ -107,6 +115,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs index 325c2040691b..6827540fa82c 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closure_ret.rs @@ -72,6 +72,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(closure.syntax().text_range()), }); Some(()) } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs index eca0ebe629f4..35b62878329f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/discriminant.rs @@ -35,7 +35,7 @@ pub(super) fn enum_hints( return None; } for variant in enum_.variant_list()?.variants() { - variant_hints(acc, sema, &variant); + variant_hints(acc, sema, &enum_, &variant); } Some(()) } @@ -43,6 +43,7 @@ pub(super) fn enum_hints( fn variant_hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, + enum_: &ast::Enum, variant: &ast::Variant, ) -> Option<()> { if variant.expr().is_some() { @@ -90,6 +91,7 @@ fn variant_hints( position: InlayHintPosition::After, pad_left: false, pad_right: false, + resolve_parent: Some(enum_.syntax().text_range()), }); Some(()) diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs deleted file mode 100644 index d3666754e2be..000000000000 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/fn_lifetime_fn.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Implementation of "lifetime elision" inlay hints: -//! ```no_run -//! fn example/* <'0> */(a: &/* '0 */()) {} -//! ``` -use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap}; -use itertools::Itertools; -use syntax::{ - ast::{self, AstNode, HasGenericParams, HasName}, - SyntaxToken, -}; -use syntax::{format_smolstr, SmolStr}; - -use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeElisionHints}; - -pub(super) fn hints( - acc: &mut Vec, - config: &InlayHintsConfig, - func: ast::Fn, -) -> Option<()> { - if config.lifetime_elision_hints == LifetimeElisionHints::Never { - return None; - } - - let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { - range: t.text_range(), - kind: InlayKind::Lifetime, - label: label.into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: true, - }; - - let param_list = func.param_list()?; - let generic_param_list = func.generic_param_list(); - let ret_type = func.ret_type(); - let self_param = param_list.self_param().filter(|it| it.amp_token().is_some()); - - let is_elided = |lt: &Option| match lt { - Some(lt) => matches!(lt.text().as_str(), "'_"), - None => true, - }; - - let potential_lt_refs = { - let mut acc: Vec<_> = vec![]; - if let Some(self_param) = &self_param { - let lifetime = self_param.lifetime(); - let is_elided = is_elided(&lifetime); - acc.push((None, self_param.amp_token(), lifetime, is_elided)); - } - param_list.params().filter_map(|it| Some((it.pat(), it.ty()?))).for_each(|(pat, ty)| { - // FIXME: check path types - walk_ty(&ty, &mut |ty| match ty { - ast::Type::RefType(r) => { - let lifetime = r.lifetime(); - let is_elided = is_elided(&lifetime); - acc.push(( - pat.as_ref().and_then(|it| match it { - ast::Pat::IdentPat(p) => p.name(), - _ => None, - }), - r.amp_token(), - lifetime, - is_elided, - )); - false - } - ast::Type::FnPtrType(_) => true, - ast::Type::PathType(t) => { - t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() - } - _ => false, - }) - }); - acc - }; - - // allocate names - let mut gen_idx_name = { - let mut gen = (0u8..).map(|idx| match idx { - idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]), - idx => format_smolstr!("'{idx}"), - }); - move || gen.next().unwrap_or_default() - }; - let mut allocated_lifetimes = vec![]; - - let mut used_names: FxHashMap = - match config.param_names_for_lifetime_elision_hints { - true => generic_param_list - .iter() - .flat_map(|gpl| gpl.lifetime_params()) - .filter_map(|param| param.lifetime()) - .filter_map(|lt| Some((SmolStr::from(lt.text().as_str().get(1..)?), 0))) - .collect(), - false => Default::default(), - }; - { - let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided); - if self_param.is_some() && potential_lt_refs.next().is_some() { - allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints { - // self can't be used as a lifetime, so no need to check for collisions - "'self".into() - } else { - gen_idx_name() - }); - } - potential_lt_refs.for_each(|(name, ..)| { - let name = match name { - Some(it) if config.param_names_for_lifetime_elision_hints => { - if let Some(c) = used_names.get_mut(it.text().as_str()) { - *c += 1; - SmolStr::from(format!("'{text}{c}", text = it.text().as_str())) - } else { - used_names.insert(it.text().as_str().into(), 0); - SmolStr::from_iter(["\'", it.text().as_str()]) - } - } - _ => gen_idx_name(), - }; - allocated_lifetimes.push(name); - }); - } - - // fetch output lifetime if elision rule applies - let output = match potential_lt_refs.as_slice() { - [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { - match lifetime { - Some(lt) => match lt.text().as_str() { - "'_" => allocated_lifetimes.first().cloned(), - "'static" => None, - name => Some(name.into()), - }, - None => allocated_lifetimes.first().cloned(), - } - } - [..] => None, - }; - - if allocated_lifetimes.is_empty() && output.is_none() { - return None; - } - - // apply hints - // apply output if required - let mut is_trivial = true; - if let (Some(output_lt), Some(r)) = (&output, ret_type) { - if let Some(ty) = r.ty() { - walk_ty(&ty, &mut |ty| match ty { - ast::Type::RefType(ty) if ty.lifetime().is_none() => { - if let Some(amp) = ty.amp_token() { - is_trivial = false; - acc.push(mk_lt_hint(amp, output_lt.to_string())); - } - false - } - ast::Type::FnPtrType(_) => true, - ast::Type::PathType(t) => { - t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() - } - _ => false, - }) - } - } - - if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial { - return None; - } - - let mut a = allocated_lifetimes.iter(); - for (_, amp_token, _, is_elided) in potential_lt_refs { - if is_elided { - let t = amp_token?; - let lt = a.next()?; - acc.push(mk_lt_hint(t, lt.to_string())); - } - } - - // generate generic param list things - match (generic_param_list, allocated_lifetimes.as_slice()) { - (_, []) => (), - (Some(gpl), allocated_lifetimes) => { - let angle_tok = gpl.l_angle_token()?; - let is_empty = gpl.generic_params().next().is_none(); - acc.push(InlayHint { - range: angle_tok.text_range(), - kind: InlayKind::Lifetime, - label: format!( - "{}{}", - allocated_lifetimes.iter().format(", "), - if is_empty { "" } else { ", " } - ) - .into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: true, - }); - } - (None, allocated_lifetimes) => acc.push(InlayHint { - range: func.name()?.syntax().text_range(), - kind: InlayKind::GenericParamList, - label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), - text_edit: None, - position: InlayHintPosition::After, - pad_left: false, - pad_right: false, - }), - } - Some(()) -} - -#[cfg(test)] -mod tests { - use crate::{ - inlay_hints::tests::{check, check_with_config, TEST_CONFIG}, - InlayHintsConfig, LifetimeElisionHints, - }; - - #[test] - fn hints_lifetimes() { - check( - r#" -fn empty() {} - -fn no_gpl(a: &()) {} - //^^^^^^<'0> - // ^'0 -fn empty_gpl<>(a: &()) {} - // ^'0 ^'0 -fn partial<'b>(a: &(), b: &'b ()) {} -// ^'0, $ ^'0 -fn partial<'a>(a: &'a (), b: &()) {} -// ^'0, $ ^'0 - -fn single_ret(a: &()) -> &() {} -// ^^^^^^^^^^<'0> - // ^'0 ^'0 -fn full_mul(a: &(), b: &()) {} -// ^^^^^^^^<'0, '1> - // ^'0 ^'1 - -fn foo<'c>(a: &'c ()) -> &() {} - // ^'c - -fn nested_in(a: & &X< &()>) {} -// ^^^^^^^^^<'0, '1, '2> - //^'0 ^'1 ^'2 -fn nested_out(a: &()) -> & &X< &()>{} -// ^^^^^^^^^^<'0> - //^'0 ^'0 ^'0 ^'0 - -impl () { - fn foo(&self) {} - // ^^^<'0> - // ^'0 - fn foo(&self) -> &() {} - // ^^^<'0> - // ^'0 ^'0 - fn foo(&self, a: &()) -> &() {} - // ^^^<'0, '1> - // ^'0 ^'1 ^'0 -} -"#, - ); - } - - #[test] - fn hints_lifetimes_named() { - check_with_config( - InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG }, - r#" -fn nested_in<'named>(named: & &X< &()>) {} -// ^'named1, 'named2, 'named3, $ - //^'named1 ^'named2 ^'named3 -"#, - ); - } - - #[test] - fn hints_lifetimes_trivial_skip() { - check_with_config( - InlayHintsConfig { - lifetime_elision_hints: LifetimeElisionHints::SkipTrivial, - ..TEST_CONFIG - }, - r#" -fn no_gpl(a: &()) {} -fn empty_gpl<>(a: &()) {} -fn partial<'b>(a: &(), b: &'b ()) {} -fn partial<'a>(a: &'a (), b: &()) {} - -fn single_ret(a: &()) -> &() {} -// ^^^^^^^^^^<'0> - // ^'0 ^'0 -fn full_mul(a: &(), b: &()) {} - -fn foo<'c>(a: &'c ()) -> &() {} - // ^'c - -fn nested_in(a: & &X< &()>) {} -fn nested_out(a: &()) -> & &X< &()>{} -// ^^^^^^^^^^<'0> - //^'0 ^'0 ^'0 ^'0 - -impl () { - fn foo(&self) {} - fn foo(&self) -> &() {} - // ^^^<'0> - // ^'0 ^'0 - fn foo(&self, a: &()) -> &() {} - // ^^^<'0, '1> - // ^'0 ^'1 ^'0 -} -"#, - ); - } - - #[test] - fn hints_lifetimes_skip_fn_likes() { - check_with_config( - InlayHintsConfig { - lifetime_elision_hints: LifetimeElisionHints::Always, - ..TEST_CONFIG - }, - r#" -fn fn_ptr(a: fn(&()) -> &()) {} -fn fn_trait<>(a: impl Fn(&()) -> &()) {} -"#, - ); - } -} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs index b60a80a8ac6b..ed7ebc3b1e7a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/generic_param.rs @@ -92,6 +92,7 @@ pub(crate) fn hints( kind: InlayKind::GenericParameter, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs index b4695a2b3519..dd4b3efeecfe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs @@ -8,9 +8,9 @@ use hir::{ db::{DefDatabase as _, HirDatabase as _}, mir::{MirSpan, TerminatorKind}, - ChalkTyInterner, DefWithBody, Semantics, + ChalkTyInterner, DefWithBody, }; -use ide_db::{FileRange, RootDatabase}; +use ide_db::{famous_defs::FamousDefs, FileRange}; use span::EditionedFileId; use syntax::{ @@ -22,16 +22,16 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, file_id: EditionedFileId, - def: &ast::Fn, + node: &ast::Fn, ) -> Option<()> { if !config.implicit_drop_hints { return None; } - let def = sema.to_def(def)?; + let def = sema.to_def(node)?; let def: DefWithBody = def.into(); let (hir, source_map) = sema.db.body_with_source_map(def.into()); @@ -121,6 +121,7 @@ pub(super) fn hints( kind: InlayKind::Drop, label, text_edit: None, + resolve_parent: Some(node.syntax().text_range()), }) } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs index 42223ddf580e..8d422478cbfc 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_static.rs @@ -3,6 +3,8 @@ //! static S: &/* 'static */str = ""; //! ``` use either::Either; +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ ast::{self, AstNode}, SyntaxKind, @@ -12,7 +14,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, statik_or_const: Either, ) -> Option<()> { if config.lifetime_elision_hints != LifetimeElisionHints::Always { @@ -38,6 +42,7 @@ pub(super) fn hints( position: InlayHintPosition::After, pad_left: false, pad_right: true, + resolve_parent: None, }); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs new file mode 100644 index 000000000000..2163c959b18a --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs @@ -0,0 +1,566 @@ +//! Implementation of "lifetime elision" inlay hints: +//! ```no_run +//! fn example/* <'0> */(a: &/* '0 */()) {} +//! ``` +use std::iter; + +use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap}; +use itertools::Itertools; +use span::EditionedFileId; +use syntax::{ + ast::{self, AstNode, HasGenericParams, HasName}, + SyntaxKind, SyntaxToken, +}; +use syntax::{format_smolstr, SmolStr}; + +use crate::{ + inlay_hints::InlayHintCtx, InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, + LifetimeElisionHints, +}; + +pub(super) fn fn_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::Fn, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + let param_list = func.param_list()?; + let generic_param_list = func.generic_param_list(); + let ret_type = func.ret_type(); + let self_param = param_list.self_param().filter(|it| it.amp_token().is_some()); + let gpl_append_range = func.name()?.syntax().text_range(); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + self_param, + |acc, allocated_lifetimes| { + acc.push(InlayHint { + range: gpl_append_range, + kind: InlayKind::GenericParamList, + label: format!("<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: false, + resolve_parent: None, + }) + }, + true, + ) +} + +pub(super) fn fn_ptr_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::FnPtrType, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + let parent_for_type = func + .syntax() + .ancestors() + .skip(1) + .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) + .find_map(ast::ForType::cast); + + let param_list = func.param_list()?; + let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); + let ret_type = func.ret_type(); + let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + None, + |acc, allocated_lifetimes| { + let has_for = for_kw.is_some(); + let for_ = if has_for { "" } else { "for" }; + acc.push(InlayHint { + range: for_kw.map_or_else( + || func.syntax().first_token().unwrap().text_range(), + |it| it.text_range(), + ), + kind: InlayKind::GenericParamList, + label: format!("{for_}<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: if has_for { + InlayHintPosition::After + } else { + InlayHintPosition::Before + }, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + }, + false, + ) +} + +pub(super) fn fn_path_hints( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + fd: &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + file_id: EditionedFileId, + func: ast::PathType, +) -> Option<()> { + if config.lifetime_elision_hints == LifetimeElisionHints::Never { + return None; + } + + // FIXME: Support general path types + let (param_list, ret_type) = func.path().as_ref().and_then(path_as_fn)?; + let parent_for_type = func + .syntax() + .ancestors() + .skip(1) + .take_while(|it| matches!(it.kind(), SyntaxKind::PAREN_TYPE | SyntaxKind::FOR_TYPE)) + .find_map(ast::ForType::cast); + + let generic_param_list = parent_for_type.as_ref().and_then(|it| it.generic_param_list()); + let for_kw = parent_for_type.as_ref().and_then(|it| it.for_token()); + hints_( + acc, + ctx, + fd, + config, + file_id, + param_list, + generic_param_list, + ret_type, + None, + |acc, allocated_lifetimes| { + let has_for = for_kw.is_some(); + let for_ = if has_for { "" } else { "for" }; + acc.push(InlayHint { + range: for_kw.map_or_else( + || func.syntax().first_token().unwrap().text_range(), + |it| it.text_range(), + ), + kind: InlayKind::GenericParamList, + label: format!("{for_}<{}>", allocated_lifetimes.iter().format(", "),).into(), + text_edit: None, + position: if has_for { + InlayHintPosition::After + } else { + InlayHintPosition::Before + }, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + }, + false, + ) +} + +fn path_as_fn(path: &ast::Path) -> Option<(ast::ParamList, Option)> { + path.segment().and_then(|it| it.param_list().zip(Some(it.ret_type()))) +} + +fn hints_( + acc: &mut Vec, + ctx: &mut InlayHintCtx, + FamousDefs(_, _): &FamousDefs<'_, '_>, + config: &InlayHintsConfig, + _file_id: EditionedFileId, + param_list: ast::ParamList, + generic_param_list: Option, + ret_type: Option, + self_param: Option, + on_missing_gpl: impl FnOnce(&mut Vec, &[SmolStr]), + mut is_trivial: bool, +) -> Option<()> { + let is_elided = |lt: &Option| match lt { + Some(lt) => matches!(lt.text().as_str(), "'_"), + None => true, + }; + + let mk_lt_hint = |t: SyntaxToken, label: String| InlayHint { + range: t.text_range(), + kind: InlayKind::Lifetime, + label: label.into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + resolve_parent: None, + }; + + let potential_lt_refs = { + let mut acc: Vec<_> = vec![]; + if let Some(self_param) = &self_param { + let lifetime = self_param.lifetime(); + let is_elided = is_elided(&lifetime); + acc.push((None, self_param.amp_token(), lifetime, is_elided)); + } + param_list + .params() + .filter_map(|it| { + Some(( + it.pat().and_then(|it| match it { + ast::Pat::IdentPat(p) => p.name(), + _ => None, + }), + it.ty()?, + )) + }) + .for_each(|(name, ty)| { + // FIXME: check path types + walk_ty(&ty, &mut |ty| match ty { + ast::Type::RefType(r) => { + let lifetime = r.lifetime(); + let is_elided = is_elided(&lifetime); + acc.push((name.clone(), r.amp_token(), lifetime, is_elided)); + false + } + ast::Type::FnPtrType(_) => { + is_trivial = false; + true + } + ast::Type::PathType(t) => { + if t.path() + .and_then(|it| it.segment()) + .and_then(|it| it.param_list()) + .is_some() + { + is_trivial = false; + true + } else { + false + } + } + _ => false, + }) + }); + acc + }; + + let mut used_names: FxHashMap = + ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).cloned().zip(iter::repeat(0)).collect(); + // allocate names + let mut gen_idx_name = { + let mut gen = (0u8..).map(|idx| match idx { + idx if idx < 10 => SmolStr::from_iter(['\'', (idx + 48) as char]), + idx => format_smolstr!("'{idx}"), + }); + let ctx = &*ctx; + move || { + gen.by_ref() + .find(|s| ctx.lifetime_stacks.iter().flat_map(|it| it.iter()).all(|n| n != s)) + .unwrap_or_default() + } + }; + let mut allocated_lifetimes = vec![]; + + { + let mut potential_lt_refs = potential_lt_refs.iter().filter(|&&(.., is_elided)| is_elided); + if self_param.is_some() && potential_lt_refs.next().is_some() { + allocated_lifetimes.push(if config.param_names_for_lifetime_elision_hints { + // self can't be used as a lifetime, so no need to check for collisions + "'self".into() + } else { + gen_idx_name() + }); + } + potential_lt_refs.for_each(|(name, ..)| { + let name = match name { + Some(it) if config.param_names_for_lifetime_elision_hints => { + if let Some(c) = used_names.get_mut(it.text().as_str()) { + *c += 1; + format_smolstr!("'{}{c}", it.text().as_str()) + } else { + used_names.insert(it.text().as_str().into(), 0); + format_smolstr!("'{}", it.text().as_str()) + } + } + _ => gen_idx_name(), + }; + allocated_lifetimes.push(name); + }); + } + + // fetch output lifetime if elision rule applies + let output = match potential_lt_refs.as_slice() { + [(_, _, lifetime, _), ..] if self_param.is_some() || potential_lt_refs.len() == 1 => { + match lifetime { + Some(lt) => match lt.text().as_str() { + "'_" => allocated_lifetimes.first().cloned(), + "'static" => None, + name => Some(name.into()), + }, + None => allocated_lifetimes.first().cloned(), + } + } + [..] => None, + }; + + if allocated_lifetimes.is_empty() && output.is_none() { + return None; + } + + // apply hints + // apply output if required + if let (Some(output_lt), Some(r)) = (&output, ret_type) { + if let Some(ty) = r.ty() { + walk_ty(&ty, &mut |ty| match ty { + ast::Type::RefType(ty) if ty.lifetime().is_none() => { + if let Some(amp) = ty.amp_token() { + is_trivial = false; + acc.push(mk_lt_hint(amp, output_lt.to_string())); + } + false + } + ast::Type::FnPtrType(_) => { + is_trivial = false; + true + } + ast::Type::PathType(t) => { + if t.path().and_then(|it| it.segment()).and_then(|it| it.param_list()).is_some() + { + is_trivial = false; + true + } else { + false + } + } + _ => false, + }) + } + } + + if config.lifetime_elision_hints == LifetimeElisionHints::SkipTrivial && is_trivial { + return None; + } + + let mut a = allocated_lifetimes.iter(); + for (_, amp_token, _, is_elided) in potential_lt_refs { + if is_elided { + let t = amp_token?; + let lt = a.next()?; + acc.push(mk_lt_hint(t, lt.to_string())); + } + } + + // generate generic param list things + match (generic_param_list, allocated_lifetimes.as_slice()) { + (_, []) => (), + (Some(gpl), allocated_lifetimes) => { + let angle_tok = gpl.l_angle_token()?; + let is_empty = gpl.generic_params().next().is_none(); + acc.push(InlayHint { + range: angle_tok.text_range(), + kind: InlayKind::Lifetime, + label: format!( + "{}{}", + allocated_lifetimes.iter().format(", "), + if is_empty { "" } else { ", " } + ) + .into(), + text_edit: None, + position: InlayHintPosition::After, + pad_left: false, + pad_right: true, + resolve_parent: None, + }); + } + (None, allocated_lifetimes) => on_missing_gpl(acc, allocated_lifetimes), + } + if let Some(stack) = ctx.lifetime_stacks.last_mut() { + stack.extend(allocated_lifetimes); + } + Some(()) +} + +#[cfg(test)] +mod tests { + use crate::{ + inlay_hints::tests::{check, check_with_config, TEST_CONFIG}, + InlayHintsConfig, LifetimeElisionHints, + }; + + #[test] + fn hints_lifetimes() { + check( + r#" +fn empty() {} + +fn no_gpl(a: &()) {} + //^^^^^^<'0> + // ^'0 +fn empty_gpl<>(a: &()) {} + // ^'0 ^'0 +fn partial<'b>(a: &(), b: &'b ()) {} +// ^'0, $ ^'0 +fn partial<'a>(a: &'a (), b: &()) {} +// ^'0, $ ^'0 + +fn single_ret(a: &()) -> &() {} +// ^^^^^^^^^^<'0> + // ^'0 ^'0 +fn full_mul(a: &(), b: &()) {} +// ^^^^^^^^<'0, '1> + // ^'0 ^'1 + +fn foo<'c>(a: &'c ()) -> &() {} + // ^'c + +fn nested_in(a: & &X< &()>) {} +// ^^^^^^^^^<'0, '1, '2> + //^'0 ^'1 ^'2 +fn nested_out(a: &()) -> & &X< &()>{} +// ^^^^^^^^^^<'0> + //^'0 ^'0 ^'0 ^'0 + +impl () { + fn foo(&self) {} + // ^^^<'0> + // ^'0 + fn foo(&self) -> &() {} + // ^^^<'0> + // ^'0 ^'0 + fn foo(&self, a: &()) -> &() {} + // ^^^<'0, '1> + // ^'0 ^'1 ^'0 +} +"#, + ); + } + + #[test] + fn hints_lifetimes_named() { + check_with_config( + InlayHintsConfig { param_names_for_lifetime_elision_hints: true, ..TEST_CONFIG }, + r#" +fn nested_in<'named>(named: & &X< &()>) {} +// ^'named1, 'named2, 'named3, $ + //^'named1 ^'named2 ^'named3 +"#, + ); + } + + #[test] + fn hints_lifetimes_trivial_skip() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::SkipTrivial, + ..TEST_CONFIG + }, + r#" +fn no_gpl(a: &()) {} +fn empty_gpl<>(a: &()) {} +fn partial<'b>(a: &(), b: &'b ()) {} +fn partial<'a>(a: &'a (), b: &()) {} + +fn single_ret(a: &()) -> &() {} +// ^^^^^^^^^^<'0> + // ^'0 ^'0 +fn full_mul(a: &(), b: &()) {} + +fn foo<'c>(a: &'c ()) -> &() {} + // ^'c + +fn nested_in(a: & &X< &()>) {} +fn nested_out(a: &()) -> & &X< &()>{} +// ^^^^^^^^^^<'0> + //^'0 ^'0 ^'0 ^'0 + +impl () { + fn foo(&self) {} + fn foo(&self) -> &() {} + // ^^^<'0> + // ^'0 ^'0 + fn foo(&self, a: &()) -> &() {} + // ^^^<'0, '1> + // ^'0 ^'1 ^'0 +} +"#, + ); + } + + #[test] + fn no_collide() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + param_names_for_lifetime_elision_hints: true, + ..TEST_CONFIG + }, + r#" +impl<'foo> { + fn foo(foo: &()) {} + // ^^^ <'foo1> + // ^ 'foo1 +} +"#, + ); + } + + #[test] + fn hints_lifetimes_fn_ptr() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + ..TEST_CONFIG + }, + r#" +fn fn_ptr(a: fn(&()) -> &fn(&()) -> &()) {} + //^^ for<'0> + //^'0 + //^'0 + //^^ for<'1> + //^'1 + //^'1 +fn fn_ptr2(a: for<'a> fn(&()) -> &()) {} + //^'0, $ + //^'0 + //^'0 +fn fn_trait(a: &impl Fn(&()) -> &()) {} +// ^^^^^^^^<'0> + // ^'0 + // ^^ for<'1> + //^'1 + // ^'1 +"#, + ); + } + + #[test] + fn hints_in_non_gen_defs() { + check_with_config( + InlayHintsConfig { + lifetime_elision_hints: LifetimeElisionHints::Always, + ..TEST_CONFIG + }, + r#" +const _: fn(&()) -> &(); + //^^ for<'0> + //^'0 + //^'0 +"#, + ); + } +} diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs index 0f3142ef3f88..28b0fa6dd4d1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/param_name.rs @@ -7,8 +7,9 @@ use std::fmt::Display; use either::Either; use hir::{Callable, Semantics}; -use ide_db::RootDatabase; +use ide_db::{famous_defs::FamousDefs, RootDatabase}; +use span::EditionedFileId; use stdx::to_lower_snake_case; use syntax::{ ast::{self, AstNode, HasArgList, HasName, UnaryOp}, @@ -19,8 +20,9 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla pub(super) fn hints( acc: &mut Vec, - sema: &Semantics<'_, RootDatabase>, + FamousDefs(sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, expr: ast::Expr, ) -> Option<()> { if !config.parameter_hints { @@ -60,6 +62,7 @@ pub(super) fn hints( position: InlayHintPosition::Before, pad_left: false, pad_right: true, + resolve_parent: Some(expr.syntax().text_range()), } }); diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs index bfb92838857c..de9b0e98a4be 100644 --- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/range_exclusive.rs @@ -3,13 +3,17 @@ //! for i in 0../* < */10 {} //! if let ../* < */100 = 50 {} //! ``` +use ide_db::famous_defs::FamousDefs; +use span::EditionedFileId; use syntax::{ast, SyntaxToken, T}; use crate::{InlayHint, InlayHintsConfig}; pub(super) fn hints( acc: &mut Vec, + FamousDefs(_sema, _): &FamousDefs<'_, '_>, config: &InlayHintsConfig, + _file_id: EditionedFileId, range: impl ast::RangeItem, ) -> Option<()> { (config.range_exclusive_hints && range.end().is_some()) @@ -30,6 +34,7 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint { kind: crate::InlayKind::RangeExclusive, label: crate::InlayHintLabel::from("<"), text_edit: None, + resolve_parent: None, } } diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs index ba0aaae19c9f..547286c3f4d6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs @@ -57,7 +57,7 @@ mod view_item_tree; mod view_memory_layout; mod view_mir; -use std::panic::UnwindSafe; +use std::{iter, panic::UnwindSafe}; use cfg::CfgOptions; use fetch_crates::CrateInfo; @@ -65,7 +65,8 @@ use hir::{sym, ChangeWithProcMacros}; use ide_db::{ base_db::{ salsa::{self, ParallelDatabase}, - CrateOrigin, Env, FileLoader, FileSet, SourceDatabase, SourceRootDatabase, VfsPath, + CrateOrigin, CrateWorkspaceData, Env, FileLoader, FileSet, SourceDatabase, + SourceRootDatabase, VfsPath, }, prime_caches, symbol_index, FxHashMap, FxIndexSet, LineIndexDatabase, }; @@ -256,9 +257,16 @@ impl Analysis { CrateOrigin::Local { repo: None, name: None }, ); change.change_file(file_id, Some(text)); - change.set_crate_graph(crate_graph); - change.set_target_data_layouts(vec![Err("fixture has no layout".into())]); - change.set_toolchains(vec![None]); + let ws_data = crate_graph + .iter() + .zip(iter::repeat(Arc::new(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: Err("fixture has no layout".into()), + toolchain: None, + }))) + .collect(); + change.set_crate_graph(crate_graph, ws_data); + host.apply_change(change); (host.analysis(), file_id) } @@ -439,12 +447,12 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - position: TextSize, + resolve_range: TextRange, hash: u64, hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe, ) -> Cancellable> { self.with_db(|db| { - inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher) + inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher) }) } diff --git a/src/tools/rust-analyzer/crates/ide/src/moniker.rs b/src/tools/rust-analyzer/crates/ide/src/moniker.rs index 4be1b570981f..14781b212969 100644 --- a/src/tools/rust-analyzer/crates/ide/src/moniker.rs +++ b/src/tools/rust-analyzer/crates/ide/src/moniker.rs @@ -223,11 +223,12 @@ pub(crate) fn def_to_kind(db: &RootDatabase, def: Definition) -> SymbolInformati Variable } } - Definition::Label(..) => Variable, // For lack of a better variant + Definition::Label(..) | Definition::InlineAsmOperand(_) => Variable, // For lack of a better variant Definition::DeriveHelper(..) => Attribute, Definition::BuiltinAttr(..) => Attribute, Definition::ToolModule(..) => Module, Definition::ExternCrateDecl(..) => Module, + Definition::InlineAsmRegOrRegClass(..) => Module, } } @@ -320,7 +321,9 @@ pub(crate) fn def_to_moniker( | Definition::DeriveHelper(_) | Definition::BuiltinLifetime(_) | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) + | Definition::InlineAsmOperand(_) => return None, Definition::Local(local) => { if !local.is_param(db) { diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 9ace9fda62b9..9bc7bf411f05 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -237,11 +237,13 @@ impl TryToNav for Definition { Definition::Trait(it) => it.try_to_nav(db), Definition::TraitAlias(it) => it.try_to_nav(db), Definition::TypeAlias(it) => it.try_to_nav(db), - Definition::ExternCrateDecl(it) => Some(it.try_to_nav(db)?), + Definition::ExternCrateDecl(it) => it.try_to_nav(db), + Definition::InlineAsmOperand(it) => it.try_to_nav(db), Definition::BuiltinLifetime(_) | Definition::BuiltinType(_) | Definition::TupleField(_) | Definition::ToolModule(_) + | Definition::InlineAsmRegOrRegClass(_) | Definition::BuiltinAttr(_) => None, // FIXME: The focus range should be set to the helper declaration Definition::DeriveHelper(it) => it.derive().try_to_nav(db), @@ -693,6 +695,31 @@ impl TryToNav for hir::ConstParam { } } +impl TryToNav for hir::InlineAsmOperand { + fn try_to_nav(&self, db: &RootDatabase) -> Option> { + let InFile { file_id, value } = &self.source(db)?; + let file_id = *file_id; + Some(orig_range_with_focus(db, file_id, value.syntax(), value.name()).map( + |(FileRange { file_id, range: full_range }, focus_range)| { + let edition = self.parent(db).module(db).krate().edition(db); + NavigationTarget { + file_id, + name: self + .name(db) + .map_or_else(|| "_".into(), |it| it.display(db, edition).to_smolstr()), + alias: None, + kind: Some(SymbolKind::Local), + full_range, + focus_range, + container_name: None, + description: None, + docs: None, + } + }, + )) + } +} + #[derive(Debug)] pub struct UpmappingResult { /// The macro call site. diff --git a/src/tools/rust-analyzer/crates/ide/src/rename.rs b/src/tools/rust-analyzer/crates/ide/src/rename.rs index 42b7472c645f..e46cb5a781f4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/rename.rs +++ b/src/tools/rust-analyzer/crates/ide/src/rename.rs @@ -2975,6 +2975,62 @@ fn test() { ); } + #[test] + fn asm_operand() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base}", + base$0 = const 0 + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + bose = const 0 + ); +} +"#, + ); + } + + #[test] + fn asm_operand2() { + check( + "bose", + r#" +//- minicore: asm +fn test() { + core::arch::asm!( + "push {base$0}", + "push {base}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + base = const 0, + boo = const 0, + ); +} +"#, + r#" +fn test() { + core::arch::asm!( + "push {bose}", + "push {bose}", + boo = const 0, + virtual_free = sym VIRTUAL_FREE, + bose = const 0, + boo = const 0, + ); +} +"#, + ); + } + #[test] fn rename_path_inside_use_tree() { check( diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs index 518e71454798..7234108701a3 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/format.rs @@ -19,21 +19,21 @@ pub(super) fn highlight_format_string( expanded_string: &ast::String, range: TextRange, ) { - if !is_format_string(expanded_string) { + if is_format_string(expanded_string) { + // FIXME: Replace this with the HIR info we have now. + lex_format_specifiers(string, &mut |piece_range, kind| { + if let Some(highlight) = highlight_format_specifier(kind) { + stack.add(HlRange { + range: piece_range + range.start(), + highlight: highlight.into(), + binding_hash: None, + }); + } + }); + return; } - // FIXME: Replace this with the HIR info we have now. - lex_format_specifiers(string, &mut |piece_range, kind| { - if let Some(highlight) = highlight_format_specifier(kind) { - stack.add(HlRange { - range: piece_range + range.start(), - highlight: highlight.into(), - binding_hash: None, - }); - } - }); - if let Some(parts) = sema.as_format_args_parts(string) { parts.into_iter().for_each(|(range, res)| { if let Some(res) = res { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index eeba9cf35c99..96375937a12e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -534,6 +534,10 @@ pub(super) fn highlight_def( Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), + Definition::InlineAsmRegOrRegClass(_) => { + Highlight::new(HlTag::Symbol(SymbolKind::InlineAsmRegOrRegClass)) + } + Definition::InlineAsmOperand(_) => Highlight::new(HlTag::Symbol(SymbolKind::Local)), }; let def_crate = def.krate(db); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index bc1ec530076c..5583f1bc8df9 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -315,6 +315,8 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::ToolModule(_) => SymbolKind::ToolModule, Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, + Definition::InlineAsmRegOrRegClass(_) => SymbolKind::InlineAsmRegOrRegClass, + Definition::InlineAsmOperand(_) => SymbolKind::Local, }; HlTag::Symbol(symbol) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index e329023606a1..3b5d1af0ac72 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -146,6 +146,7 @@ impl HlTag { SymbolKind::Field => "field", SymbolKind::Function => "function", SymbolKind::Impl => "self_type", + SymbolKind::InlineAsmRegOrRegClass => "reg", SymbolKind::Label => "label", SymbolKind::LifetimeParam => "lifetime", SymbolKind::Local => "variable", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html new file mode 100644 index 000000000000..15a6386aa3c8 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -0,0 +1,119 @@ + + +
fn main() {
+    unsafe {
+        let foo = 1;
+        let mut o = 0;
+        core::arch::asm!(
+            "%input = OpLoad _ {0}",
+            concat!("%result = ", "bar", " _ %input"),
+            "OpStore {1} %result",
+            in(reg) &foo,
+            in(reg) &mut o,
+        );
+
+        let thread_id: usize;
+        core::arch::asm!("
+            mov {0}, gs:[0x30]
+            mov {0}, [{0}+0x48]
+        ", out(reg) thread_id, options(pure, readonly, nostack));
+
+        static UNMAP_BASE: usize;
+        const MEM_RELEASE: usize;
+        static VirtualFree: usize;
+        const OffPtr: usize;
+        const OffFn: usize;
+        core::arch::asm!("
+            push {free_type}
+            push {free_size}
+            push {base}
+
+            mov eax, fs:[30h]
+            mov eax, [eax+8h]
+            add eax, {off_fn}
+            mov [eax-{off_fn}+{off_ptr}], eax
+
+            push eax
+
+            jmp {virtual_free}
+            ",
+            off_ptr = const OffPtr,
+            off_fn  = const OffFn,
+
+            free_size = const 0,
+            free_type = const MEM_RELEASE,
+
+            virtual_free = sym VirtualFree,
+
+            base = sym UNMAP_BASE,
+            options(noreturn),
+        );
+    }
+}
+// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274
+#[inline]
+pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! {
+    // Ensure thumb mode is set.
+    let rv = (rv as u32) | 1;
+    let msp = msp as u32;
+    core::arch::asm!(
+        "mrs {tmp}, CONTROL",
+        "bics {tmp}, {spsel}",
+        "msr CONTROL, {tmp}",
+        "isb",
+        "msr MSP, {msp}",
+        "bx {rv}",
+        // `out(reg) _` is not permitted in a `noreturn` asm! call,
+        // so instead use `in(reg) 0` and don't restore it afterwards.
+        tmp = in(reg) 0,
+        spsel = in(reg) 2,
+        msp = in(reg) msp,
+        rv = in(reg) rv,
+        options(noreturn, nomem, nostack),
+    );
+}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html new file mode 100644 index 000000000000..d07ba74db249 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html @@ -0,0 +1,53 @@ + + +
fn main() {
+    template!(template);
+}
+
+#[proc_macros::issue_18089]
+fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html new file mode 100644 index 000000000000..a790b385786d --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html new file mode 100644 index 000000000000..4ccc40799088 --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html @@ -0,0 +1,74 @@ + + +
extern crate self;
+
+use crate;
+use self;
+mod __ {
+    use super::*;
+}
+
+macro_rules! void {
+    ($($tt:tt)*) => {}
+}
+
+struct __ where Self:;
+fn __(_: Self) {}
+void!(Self);
+
+// edition dependent
+void!(try async await gen);
+// edition and context dependent
+void!(dyn);
+// builtin custom syntax
+void!(builtin offset_of format_args asm);
+// contextual
+void!(macro_rules, union, default, raw, auto, yeet);
+// reserved
+void!(abstract become box do final macro override priv typeof unsized virtual yield);
+void!('static 'self 'unsafe)
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index cb47fc68bc1b..5594a36e7318 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -165,16 +165,16 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( - "mov {0}, {1}", - "add {0}, 5", - out(reg) o, - in(reg) i, + core::arch::asm!( + "mov {0}, {1}", + "add {0}, 5", + out(reg) o, + in(reg) i, ); const CONSTANT: () = (): let mut m = (); format_args!(concat!("{}"), "{}"); - format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); - reuse_twice!("{backslash}"); + format_args!("{} {} {} {} {} {} {backslash} {CONSTANT} {m}", backslash, format_args!("{}", 0), foo, "bar", toho!(), backslash); + reuse_twice!("{backslash}"); } \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs index 2070022d4183..94cee4ef43b1 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tests.rs @@ -2,6 +2,7 @@ use std::time::Instant; use expect_test::{expect_file, ExpectFile}; use ide_db::SymbolKind; +use span::Edition; use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; use crate::{fixture, FileRange, HighlightConfig, HlTag, TextRange}; @@ -383,8 +384,10 @@ where #[test] fn test_keyword_highlighting() { - check_highlighting( - r#" + for edition in Edition::iter() { + check_highlighting( + &(format!("//- /main.rs crate:main edition:{edition}") + + r#" extern crate self; use crate; @@ -396,13 +399,27 @@ mod __ { macro_rules! void { ($($tt:tt)*) => {} } -void!(Self); + struct __ where Self:; fn __(_: Self) {} -"#, - expect_file!["./test_data/highlight_keywords.html"], - false, - ); +void!(Self); + +// edition dependent +void!(try async await gen); +// edition and context dependent +void!(dyn); +// builtin custom syntax +void!(builtin offset_of format_args asm); +// contextual +void!(macro_rules, union, default, raw, auto, yeet); +// reserved +void!(abstract become box do final macro override priv typeof unsized virtual yield); +void!('static 'self 'unsafe) +"#), + expect_file![format!("./test_data/highlight_keywords_{edition}.html")], + false, + ); + } } #[test] @@ -532,7 +549,7 @@ fn main() { toho!("{}fmt", 0); let i: u64 = 3; let o: u64; - asm!( + core::arch::asm!( "mov {0}, {1}", "add {0}, 5", out(reg) o, @@ -554,6 +571,7 @@ fn main() { fn test_unsafe_highlighting() { check_highlighting( r#" +//- minicore: sized macro_rules! id { ($($tt:tt)*) => { $($tt)* @@ -1257,3 +1275,103 @@ fn f<'de, T: Deserialize<'de>>() { ); let _ = analysis.highlight(HL_CONFIG, file_id).unwrap(); } + +#[test] +fn test_asm_highlighting() { + check_highlighting( + r#" +//- minicore: asm, concat +fn main() { + unsafe { + let foo = 1; + let mut o = 0; + core::arch::asm!( + "%input = OpLoad _ {0}", + concat!("%result = ", "bar", " _ %input"), + "OpStore {1} %result", + in(reg) &foo, + in(reg) &mut o, + ); + + let thread_id: usize; + core::arch::asm!(" + mov {0}, gs:[0x30] + mov {0}, [{0}+0x48] + ", out(reg) thread_id, options(pure, readonly, nostack)); + + static UNMAP_BASE: usize; + const MEM_RELEASE: usize; + static VirtualFree: usize; + const OffPtr: usize; + const OffFn: usize; + core::arch::asm!(" + push {free_type} + push {free_size} + push {base} + + mov eax, fs:[30h] + mov eax, [eax+8h] + add eax, {off_fn} + mov [eax-{off_fn}+{off_ptr}], eax + + push eax + + jmp {virtual_free} + ", + off_ptr = const OffPtr, + off_fn = const OffFn, + + free_size = const 0, + free_type = const MEM_RELEASE, + + virtual_free = sym VirtualFree, + + base = sym UNMAP_BASE, + options(noreturn), + ); + } +} +// taken from https://github.com/rust-embedded/cortex-m/blob/47921b51f8b960344fcfa1255a50a0d19efcde6d/cortex-m/src/asm.rs#L254-L274 +#[inline] +pub unsafe fn bootstrap(msp: *const u32, rv: *const u32) -> ! { + // Ensure thumb mode is set. + let rv = (rv as u32) | 1; + let msp = msp as u32; + core::arch::asm!( + "mrs {tmp}, CONTROL", + "bics {tmp}, {spsel}", + "msr CONTROL, {tmp}", + "isb", + "msr MSP, {msp}", + "bx {rv}", + // `out(reg) _` is not permitted in a `noreturn` asm! call, + // so instead use `in(reg) 0` and don't restore it afterwards. + tmp = in(reg) 0, + spsel = in(reg) 2, + msp = in(reg) msp, + rv = in(reg) rv, + options(noreturn, nomem, nostack), + ); +} +"#, + expect_file!["./test_data/highlight_asm.html"], + false, + ); +} + +#[test] +fn issue_18089() { + check_highlighting( + r#" +//- proc_macros: issue_18089 +fn main() { + template!(template); +} + +#[proc_macros::issue_18089] +fn template() {} +"#, + expect_file!["./test_data/highlight_issue_18089.html"], + false, + ); +} diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index c066f5048915..a62cbc7fb29f 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -15,7 +15,7 @@ macro_rules! define_symbols { (@WITH_NAME: $($alias:ident = $value:literal,)* @PLAIN: $($name:ident,)*) => { // Ideally we would be emitting `const` here, but then we no longer have stable addresses // which is what we are relying on for equality! In the future if consts can refer to - // statics we should swap these for `const`s and have the the string literal being pointed + // statics we should swap these for `const`s and have the string literal being pointed // to be statics to refer to such that their address is stable. $( pub static $name: Symbol = Symbol { repr: TaggedArcPtr::non_arc(&stringify!($name)) }; @@ -243,6 +243,7 @@ define_symbols! { future_output, Future, ge, + generic_associated_type_extended, get_context, global_allocator, global_asm, diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs index abad7e9f7d22..baa45174236c 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -10,7 +10,7 @@ use hir_expand::proc_macro::{ ProcMacros, }; use ide_db::{ - base_db::{CrateGraph, Env, SourceRoot, SourceRootId}, + base_db::{CrateGraph, CrateWorkspaceData, Env, SourceRoot, SourceRootId}, prime_caches, ChangeWithProcMacros, FxHashMap, RootDatabase, }; use itertools::Itertools; @@ -447,12 +447,16 @@ fn load_crate_graph( let source_roots = source_root_config.partition(vfs); analysis_change.set_roots(source_roots); - let num_crates = crate_graph.len(); - analysis_change.set_crate_graph(crate_graph); + let ws_data = crate_graph + .iter() + .zip(iter::repeat(From::from(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: target_layout.clone(), + toolchain: toolchain.clone(), + }))) + .collect(); + analysis_change.set_crate_graph(crate_graph, ws_data); analysis_change.set_proc_macros(proc_macros); - analysis_change - .set_target_data_layouts(iter::repeat(target_layout.clone()).take(num_crates).collect()); - analysis_change.set_toolchains(iter::repeat(toolchain.clone()).take(num_crates).collect()); db.apply_change(analysis_change); db @@ -489,8 +493,17 @@ impl ProcMacroExpander for Expander { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result, ProcMacroExpansionError> { - match self.0.expand(subtree, attrs, env.clone(), def_site, call_site, mixed_site) { + match self.0.expand( + subtree, + attrs, + env.clone(), + def_site, + call_site, + mixed_site, + current_dir, + ) { Ok(Ok(subtree)) => Ok(subtree), Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err.0)), Err(err) => Err(ProcMacroExpansionError::System(err.to_string())), diff --git a/src/tools/rust-analyzer/crates/mbe/Cargo.toml b/src/tools/rust-analyzer/crates/mbe/Cargo.toml index 756d42ef573f..b37d57f28012 100644 --- a/src/tools/rust-analyzer/crates/mbe/Cargo.toml +++ b/src/tools/rust-analyzer/crates/mbe/Cargo.toml @@ -18,6 +18,7 @@ rustc-hash.workspace = true smallvec.workspace = true tracing.workspace = true arrayvec.workspace = true +ra-ap-rustc_lexer.workspace = true # local deps syntax.workspace = true @@ -30,6 +31,7 @@ syntax-bridge.workspace = true [dev-dependencies] test-utils.workspace = true +expect-test.workspace = true [features] in-rust-tree = ["parser/in-rust-tree", "tt/in-rust-tree", "syntax/in-rust-tree"] diff --git a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs index 43604eb232dc..9e4ef1407408 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/benchmark.rs @@ -146,7 +146,7 @@ fn invocation_fixtures( Some(MetaVarKind::Pat) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Path) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Literal) => token_trees.push(make_literal("1")), - Some(MetaVarKind::Expr) => token_trees.push(make_ident("foo")), + Some(MetaVarKind::Expr(_)) => token_trees.push(make_ident("foo")), Some(MetaVarKind::Lifetime) => { token_trees.push(make_punct('\'')); token_trees.push(make_ident("a")); @@ -216,7 +216,11 @@ fn invocation_fixtures( token_trees.push(subtree.into()); } - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => {} + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. } => {} }; // Simple linear congruential generator for deterministic result diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index e69d7d14e253..95641abc0f37 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -69,7 +69,7 @@ use tt::{iter::TtIter, DelimSpan}; use crate::{ expander::{Binding, Bindings, ExpandResult, Fragment}, expect_fragment, - parser::{MetaVarKind, Op, RepeatKind, Separator}, + parser::{ExprKind, MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandErrorKind, MetaTemplate, ValueResult, }; @@ -584,7 +584,11 @@ fn match_loop_inner<'t>( error_items.push(item); } OpDelimited::Op( - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. }, + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. }, ) => { stdx::never!("metavariable expression in lhs found"); } @@ -769,23 +773,28 @@ fn match_meta_var( it.map(|it| tt::TokenTree::subtree_or_wrap(it, delim_span)).map(Fragment::Path) }); } - MetaVarKind::Expr => { - // `expr` should not match underscores, let expressions, or inline const. The latter - // two are for [backwards compatibility][0]. + MetaVarKind::Expr(expr) => { + // `expr_2021` should not match underscores, let expressions, or inline const. + // The latter two are for [backwards compatibility][0]. + // And `expr` also should not contain let expressions but may contain the other two + // since `Edition2024`. // HACK: Macro expansion should not be done using "rollback and try another alternative". // rustc [explicitly checks the next token][1]. // [0]: https://github.com/rust-lang/rust/issues/86730 // [1]: https://github.com/rust-lang/rust/blob/f0c4da499/compiler/rustc_expand/src/mbe/macro_parser.rs#L576 match input.peek_n(0) { - Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) - if it.sym == sym::underscore - || it.sym == sym::let_ - || it.sym == sym::const_ => - { - return ExpandResult::only_err(ExpandError::new( - it.span, - ExpandErrorKind::NoMatchingRule, - )) + Some(tt::TokenTree::Leaf(tt::Leaf::Ident(it))) => { + let is_err = if it.is_raw.no() && matches!(expr, ExprKind::Expr2021) { + it.sym == sym::underscore || it.sym == sym::let_ || it.sym == sym::const_ + } else { + it.sym == sym::let_ + }; + if is_err { + return ExpandResult::only_err(ExpandError::new( + it.span, + ExpandErrorKind::NoMatchingRule, + )); + } } _ => {} }; @@ -874,7 +883,11 @@ fn collect_vars(collector_fun: &mut impl FnMut(Symbol), pattern: &MetaTemplate) Op::Subtree { tokens, .. } => collect_vars(collector_fun, tokens), Op::Repeat { tokens, .. } => collect_vars(collector_fun, tokens), Op::Literal(_) | Op::Ident(_) | Op::Punct(_) => {} - Op::Ignore { .. } | Op::Index { .. } | Op::Count { .. } | Op::Len { .. } => { + Op::Ignore { .. } + | Op::Index { .. } + | Op::Count { .. } + | Op::Len { .. } + | Op::Concat { .. } => { stdx::never!("metavariable expression in lhs found"); } } diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs index 286bd748cbe7..1db2f35d2623 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/transcriber.rs @@ -2,12 +2,12 @@ //! `$ident => foo`, interpolates variables in the template, to get `fn foo() {}` use intern::{sym, Symbol}; -use span::Span; +use span::{Edition, Span}; use tt::Delimiter; use crate::{ expander::{Binding, Bindings, Fragment}, - parser::{MetaVarKind, Op, RepeatKind, Separator}, + parser::{ConcatMetaVarExprElem, MetaVarKind, Op, RepeatKind, Separator}, ExpandError, ExpandErrorKind, ExpandResult, MetaTemplate, }; @@ -97,7 +97,7 @@ impl Bindings { | MetaVarKind::Ty | MetaVarKind::Pat | MetaVarKind::PatParam - | MetaVarKind::Expr + | MetaVarKind::Expr(_) | MetaVarKind::Ident => { Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { sym: sym::missing.clone(), @@ -312,6 +312,82 @@ fn expand_subtree( .into(), ); } + Op::Concat { elements, span: concat_span } => { + let mut concatenated = String::new(); + for element in elements { + match element { + ConcatMetaVarExprElem::Ident(ident) => { + concatenated.push_str(ident.sym.as_str()) + } + ConcatMetaVarExprElem::Literal(lit) => { + // FIXME: This isn't really correct wrt. escaping, but that's what rustc does and anyway + // escaping is used most of the times for characters that are invalid in identifiers. + concatenated.push_str(lit.symbol.as_str()) + } + ConcatMetaVarExprElem::Var(var) => { + // Handling of repetitions in `${concat}` isn't fleshed out in rustc, so we currently + // err at it. + // FIXME: Do what rustc does for repetitions. + let var_value = match ctx.bindings.get_fragment( + &var.sym, + var.span, + &mut ctx.nesting, + marker, + ) { + Ok(var) => var, + Err(e) => { + if err.is_none() { + err = Some(e); + }; + continue; + } + }; + let value = match &var_value { + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Ident(ident))) => { + ident.sym.as_str() + } + Fragment::Tokens(tt::TokenTree::Leaf(tt::Leaf::Literal(lit))) => { + lit.symbol.as_str() + } + _ => { + if err.is_none() { + err = Some(ExpandError::binding_error(var.span, "metavariables of `${concat(..)}` must be of type `ident`, `literal` or `tt`")) + } + continue; + } + }; + concatenated.push_str(value); + } + } + } + + // `${concat}` span comes from the macro (at least for now). + // See https://github.com/rust-lang/rust/blob/b0af276da341/compiler/rustc_expand/src/mbe/transcribe.rs#L724-L726. + let mut result_span = *concat_span; + marker(&mut result_span); + + // FIXME: NFC normalize the result. + if !rustc_lexer::is_ident(&concatenated) { + if err.is_none() { + err = Some(ExpandError::binding_error( + *concat_span, + "`${concat(..)}` is not generating a valid identifier", + )); + } + // Insert a dummy identifier for better parsing. + concatenated.clear(); + concatenated.push_str("__ra_concat_dummy"); + } + + let needs_raw = + parser::SyntaxKind::from_keyword(&concatenated, Edition::LATEST).is_some(); + let is_raw = if needs_raw { tt::IdentIsRaw::Yes } else { tt::IdentIsRaw::No }; + arena.push(tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + is_raw, + span: result_span, + sym: Symbol::intern(&concatenated), + }))); + } } } // drain the elements added in this instance of expand_subtree diff --git a/src/tools/rust-analyzer/crates/mbe/src/lib.rs b/src/tools/rust-analyzer/crates/mbe/src/lib.rs index 88785537c7d2..ca10a2be2732 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/lib.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/lib.rs @@ -6,11 +6,20 @@ //! The tests for this functionality live in another crate: //! `hir_def::macro_expansion_tests::mbe`. +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(not(feature = "in-rust-tree"))] +extern crate ra_ap_rustc_lexer as rustc_lexer; +#[cfg(feature = "in-rust-tree")] +extern crate rustc_lexer; + mod expander; mod parser; #[cfg(test)] mod benchmark; +#[cfg(test)] +mod tests; use span::{Edition, Span, SyntaxContextId}; use syntax_bridge::to_parser_input; diff --git a/src/tools/rust-analyzer/crates/mbe/src/parser.rs b/src/tools/rust-analyzer/crates/mbe/src/parser.rs index 218c04640f12..b55edf4a5e0c 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/parser.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/parser.rs @@ -84,6 +84,10 @@ pub(crate) enum Op { // FIXME: `usize`` once we drop support for 1.76 depth: Option, }, + Concat { + elements: Box<[ConcatMetaVarExprElem]>, + span: Span, + }, Repeat { tokens: MetaTemplate, kind: RepeatKind, @@ -98,6 +102,18 @@ pub(crate) enum Op { Ident(tt::Ident), } +#[derive(Clone, Debug, PartialEq, Eq)] +pub(crate) enum ConcatMetaVarExprElem { + /// There is NO preceding dollar sign, which means that this identifier should be interpreted + /// as a literal. + Ident(tt::Ident), + /// There is a preceding dollar sign, which means that this identifier should be expanded + /// and interpreted as a variable. + Var(tt::Ident), + /// For example, a number or a string. + Literal(tt::Literal), +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum RepeatKind { ZeroOrMore, @@ -105,6 +121,16 @@ pub(crate) enum RepeatKind { ZeroOrOne, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) enum ExprKind { + // Matches expressions using the post-edition 2024. Was written using + // `expr` in edition 2024 or later. + Expr, + // Matches expressions using the pre-edition 2024 rules. + // Either written using `expr` in edition 2021 or earlier or.was written using `expr_2021`. + Expr2021, +} + #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub(crate) enum MetaVarKind { Path, @@ -116,7 +142,7 @@ pub(crate) enum MetaVarKind { Meta, Item, Vis, - Expr, + Expr(ExprKind), Ident, Tt, Lifetime, @@ -277,17 +303,27 @@ fn eat_fragment_kind( let kind = match ident.sym.as_str() { "path" => MetaVarKind::Path, "ty" => MetaVarKind::Ty, - "pat" => match edition(ident.span.ctx) { - Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam, - Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat, - }, + "pat" => { + if edition(ident.span.ctx).at_least_2021() { + MetaVarKind::Pat + } else { + MetaVarKind::PatParam + } + } "pat_param" => MetaVarKind::PatParam, "stmt" => MetaVarKind::Stmt, "block" => MetaVarKind::Block, "meta" => MetaVarKind::Meta, "item" => MetaVarKind::Item, "vis" => MetaVarKind::Vis, - "expr" => MetaVarKind::Expr, + "expr" => { + if edition(ident.span.ctx).at_least_2024() { + MetaVarKind::Expr(ExprKind::Expr) + } else { + MetaVarKind::Expr(ExprKind::Expr2021) + } + } + "expr_2021" => MetaVarKind::Expr(ExprKind::Expr2021), "ident" => MetaVarKind::Ident, "tt" => MetaVarKind::Tt, "lifetime" => MetaVarKind::Lifetime, @@ -364,6 +400,32 @@ fn parse_metavar_expr(src: &mut TtIter<'_, Span>) -> Result { let depth = if try_eat_comma(&mut args) { Some(parse_depth(&mut args)?) } else { None }; Op::Count { name: ident.sym.clone(), depth } } + s if sym::concat == *s => { + let mut elements = Vec::new(); + while let Some(next) = args.peek_n(0) { + let element = if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = next { + args.next().expect("already peeked"); + ConcatMetaVarExprElem::Literal(lit.clone()) + } else { + let is_var = try_eat_dollar(&mut args); + let ident = args.expect_ident_or_underscore()?.clone(); + + if is_var { + ConcatMetaVarExprElem::Var(ident) + } else { + ConcatMetaVarExprElem::Ident(ident) + } + }; + elements.push(element); + if args.peek_n(0).is_some() { + args.expect_comma()?; + } + } + if elements.len() < 2 { + return Err(()); + } + Op::Concat { elements: elements.into_boxed_slice(), span: func.span } + } _ => return Err(()), }; @@ -394,3 +456,11 @@ fn try_eat_comma(src: &mut TtIter<'_, Span>) -> bool { } false } + +fn try_eat_dollar(src: &mut TtIter<'_, Span>) -> bool { + if let Some(tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. }))) = src.peek_n(0) { + let _ = src.next(); + return true; + } + false +} diff --git a/src/tools/rust-analyzer/crates/mbe/src/tests.rs b/src/tools/rust-analyzer/crates/mbe/src/tests.rs new file mode 100644 index 000000000000..e63ad113ffd2 --- /dev/null +++ b/src/tools/rust-analyzer/crates/mbe/src/tests.rs @@ -0,0 +1,332 @@ +//! Tests specific to declarative macros, aka macros by example. This covers +//! both stable `macro_rules!` macros as well as unstable `macro` macros. +// FIXME: Move more of the nameres independent tests from +// crates\hir-def\src\macro_expansion_tests\mod.rs to this +use expect_test::expect; +use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use stdx::format_to; +use tt::{TextRange, TextSize}; + +use crate::DeclarativeMacro; + +#[expect(deprecated)] +fn check_( + def_edition: Edition, + call_edition: Edition, + macro2: bool, + decl: &str, + arg: &str, + render_debug: bool, + expect: expect_test::Expect, + parse: parser::TopEntryPoint, +) { + let decl_tt = &syntax_bridge::parse_to_token_tree( + def_edition, + SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), def_edition), + ast_id: ErasedFileAstId::from_raw(0), + }, + SyntaxContextId::ROOT, + decl, + ) + .unwrap(); + let mac = if macro2 { + DeclarativeMacro::parse_macro2(None, decl_tt, |_| def_edition) + } else { + DeclarativeMacro::parse_macro_rules(decl_tt, |_| def_edition) + }; + let call_anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(1), call_edition), + ast_id: ErasedFileAstId::from_raw(0), + }; + let arg_tt = + syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg) + .unwrap(); + let res = mac.expand( + &arg_tt, + |_| (), + Span { + range: TextRange::up_to(TextSize::of(arg)), + anchor: call_anchor, + ctx: SyntaxContextId::ROOT, + }, + def_edition, + ); + let mut expect_res = String::new(); + if let Some(err) = res.err { + format_to!(expect_res, "{err:#?}\n\n",); + } + if render_debug { + format_to!(expect_res, "{:#?}\n\n", res.value.0); + } + let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition); + format_to!( + expect_res, + "{}", + syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( + node.syntax_node(), + &mut |it| it.clone() + ) + ); + expect.assert_eq(&expect_res); +} + +fn check( + def_edition: Edition, + call_edition: Edition, + decl: &str, + arg: &str, + expect: expect_test::Expect, +) { + check_( + def_edition, + call_edition, + false, + decl, + arg, + true, + expect, + parser::TopEntryPoint::SourceFile, + ); +} + +#[test] +fn token_mapping_smoke_test() { + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +( struct $ident:ident ) => { + struct $ident { + map: ::std::collections::HashSet<()>, + } +}; +"#, + r#" +struct MyTraitMap2 +"#, + expect![[r#" + SUBTREE $$ 1:0@0..20#0 1:0@0..20#0 + IDENT struct 0:0@34..40#0 + IDENT MyTraitMap2 1:0@8..19#0 + SUBTREE {} 0:0@48..49#0 0:0@100..101#0 + IDENT map 0:0@58..61#0 + PUNCH : [alone] 0:0@61..62#0 + PUNCH : [joint] 0:0@63..64#0 + PUNCH : [alone] 0:0@64..65#0 + IDENT std 0:0@65..68#0 + PUNCH : [joint] 0:0@68..69#0 + PUNCH : [alone] 0:0@69..70#0 + IDENT collections 0:0@70..81#0 + PUNCH : [joint] 0:0@81..82#0 + PUNCH : [alone] 0:0@82..83#0 + IDENT HashSet 0:0@83..90#0 + PUNCH < [alone] 0:0@90..91#0 + SUBTREE () 0:0@91..92#0 0:0@92..93#0 + PUNCH > [joint] 0:0@93..94#0 + PUNCH , [alone] 0:0@94..95#0 + + struct MyTraitMap2 { + map: ::std::collections::HashSet<()>, + }"#]], + ); +} + +#[test] +fn token_mapping_floats() { + // Regression test for https://github.com/rust-lang/rust-analyzer/issues/12216 + // (and related issues) + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +($($tt:tt)*) => { + $($tt)* +}; +"#, + r#" +fn main() { + 1; + 1.0; + ((1,),).0.0; + let x = 1; +} +"#, + expect![[r#" + SUBTREE $$ 1:0@0..63#0 1:0@0..63#0 + IDENT fn 1:0@1..3#0 + IDENT main 1:0@4..8#0 + SUBTREE () 1:0@8..9#0 1:0@9..10#0 + SUBTREE {} 1:0@11..12#0 1:0@61..62#0 + LITERAL Integer 1 1:0@17..18#0 + PUNCH ; [alone] 1:0@18..19#0 + LITERAL Float 1.0 1:0@24..27#0 + PUNCH ; [alone] 1:0@27..28#0 + SUBTREE () 1:0@33..34#0 1:0@39..40#0 + SUBTREE () 1:0@34..35#0 1:0@37..38#0 + LITERAL Integer 1 1:0@35..36#0 + PUNCH , [alone] 1:0@36..37#0 + PUNCH , [alone] 1:0@38..39#0 + PUNCH . [alone] 1:0@40..41#0 + LITERAL Float 0.0 1:0@41..44#0 + PUNCH ; [alone] 1:0@44..45#0 + IDENT let 1:0@50..53#0 + IDENT x 1:0@54..55#0 + PUNCH = [alone] 1:0@56..57#0 + LITERAL Integer 1 1:0@58..59#0 + PUNCH ; [alone] 1:0@59..60#0 + + fn main(){ + 1; + 1.0; + ((1,),).0.0; + let x = 1; + }"#]], + ); +} + +#[test] +fn expr_2021() { + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, + const { 1 }, +"#, + expect![[r#" + SUBTREE $$ 1:0@0..25#0 1:0@0..25#0 + IDENT _ 1:0@5..6#0 + PUNCH ; [joint] 0:0@36..37#0 + SUBTREE () 0:0@34..35#0 0:0@34..35#0 + IDENT const 1:0@12..17#0 + SUBTREE {} 1:0@18..19#0 1:0@22..23#0 + LITERAL Integer 1 1:0@20..21#0 + PUNCH ; [alone] 0:0@39..40#0 + + _; + (const { + 1 + });"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..6#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 + PUNCH ; [alone] 0:0@39..40#0 + + ;"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + const { 1 }, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..10#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..18#0 1:0@0..18#0 + PUNCH ; [alone] 0:0@39..40#0 + + ;"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + 4, + "literal", + funcall(), + future.await, + break 'foo bar, +"#, + expect![[r#" + SUBTREE $$ 1:0@0..76#0 1:0@0..76#0 + LITERAL Integer 4 1:0@5..6#0 + PUNCH ; [joint] 0:0@41..42#0 + LITERAL Str literal 1:0@12..21#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT funcall 1:0@27..34#0 + SUBTREE () 1:0@34..35#0 1:0@35..36#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT future 1:0@42..48#0 + PUNCH . [alone] 1:0@48..49#0 + IDENT await 1:0@49..54#0 + PUNCH ; [joint] 0:0@41..42#0 + SUBTREE () 0:0@39..40#0 0:0@39..40#0 + IDENT break 1:0@60..65#0 + PUNCH ' [joint] 1:0@66..67#0 + IDENT foo 1:0@67..70#0 + IDENT bar 1:0@71..74#0 + PUNCH ; [alone] 0:0@44..45#0 + + 4; + "literal"; + (funcall()); + (future.await); + (break 'foo bar);"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:0@5..6#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:0@0..8#0 1:0@0..8#0 + PUNCH ; [alone] 0:0@44..45#0 + + ;"#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/src/edition.rs b/src/tools/rust-analyzer/crates/parser/src/edition.rs index be0a2c794e58..702b16252d4e 100644 --- a/src/tools/rust-analyzer/crates/parser/src/edition.rs +++ b/src/tools/rust-analyzer/crates/parser/src/edition.rs @@ -30,6 +30,12 @@ impl Edition { pub fn at_least_2018(self) -> bool { self >= Edition::Edition2018 } + + pub fn iter() -> impl Iterator { + [Edition::Edition2015, Edition::Edition2018, Edition::Edition2021, Edition::Edition2024] + .iter() + .copied() + } } #[derive(Debug)] diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs index a678c1f3a70e..2333e6c862be 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs @@ -245,7 +245,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { // test builtin_expr // fn foo() { -// builtin#asm(0); +// builtin#asm(""); // builtin#format_args("", 0, 1, a = 2 + 3, a + b); // builtin#offset_of(Foo, bar.baz.0); // } @@ -297,18 +297,188 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option { p.expect(T![')']); Some(m.complete(p, FORMAT_ARGS_EXPR)) } else if p.at_contextual_kw(T![asm]) { - p.bump_remap(T![asm]); - p.expect(T!['(']); - // FIXME: We just put expression here so highlighting kind of keeps working - expr(p); - p.expect(T![')']); - Some(m.complete(p, ASM_EXPR)) + parse_asm_expr(p, m) } else { m.abandon(p); None } } +// test asm_expr +// fn foo() { +// builtin#asm( +// "mov {tmp}, {x}", +// "shl {tmp}, 1", +// "shl {x}, 2", +// "add {x}, {tmp}", +// x = inout(reg) x, +// tmp = out(reg) _, +// ); +// } +fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option { + p.bump_remap(T![asm]); + p.expect(T!['(']); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + let mut allow_templates = true; + while !p.at(EOF) && !p.at(T![')']) { + p.expect(T![,]); + // accept trailing commas + if p.at(T![')']) { + break; + } + + let op_n = p.start(); + // Parse clobber_abi + if p.eat_contextual_kw(T![clobber_abi]) { + parse_clobber_abi(p); + op_n.complete(p, ASM_CLOBBER_ABI); + allow_templates = false; + continue; + } + + // Parse options + if p.eat_contextual_kw(T![options]) { + parse_options(p); + op_n.complete(p, ASM_OPTIONS); + allow_templates = false; + continue; + } + + // Parse operand names + if p.at(T![ident]) && p.nth_at(1, T![=]) { + name(p); + p.bump(T![=]); + allow_templates = false; + true + } else { + false + }; + + let op = p.start(); + let dir_spec = p.start(); + if p.eat(T![in]) || p.eat_contextual_kw(T![out]) || p.eat_contextual_kw(T![lateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); + parse_reg(p); + let op_expr = p.start(); + expr(p); + op_expr.complete(p, ASM_OPERAND_EXPR); + op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![inout]) || p.eat_contextual_kw(T![inlateout]) { + dir_spec.complete(p, ASM_DIR_SPEC); + parse_reg(p); + let op_expr = p.start(); + expr(p); + if p.eat(T![=>]) { + expr(p); + } + op_expr.complete(p, ASM_OPERAND_EXPR); + op.complete(p, ASM_REG_OPERAND); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![label]) { + dir_spec.abandon(p); + block_expr(p); + op.complete(p, ASM_OPERAND_NAMED); + op_n.complete(p, ASM_LABEL); + } else if p.eat(T![const]) { + dir_spec.abandon(p); + expr(p); + op.complete(p, ASM_CONST); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if p.eat_contextual_kw(T![sym]) { + dir_spec.abandon(p); + paths::type_path(p); + op.complete(p, ASM_SYM); + op_n.complete(p, ASM_OPERAND_NAMED); + } else if allow_templates { + dir_spec.abandon(p); + op.abandon(p); + op_n.abandon(p); + if expr(p).is_none() { + p.err_and_bump("expected asm template"); + } + continue; + } else { + dir_spec.abandon(p); + op.abandon(p); + op_n.abandon(p); + p.err_and_bump("expected asm operand"); + if p.at(T!['}']) { + break; + } + continue; + }; + allow_templates = false; + } + p.expect(T![')']); + Some(m.complete(p, ASM_EXPR)) +} + +fn parse_options(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + const OPTIONS: &[SyntaxKind] = &[ + T![pure], + T![nomem], + T![readonly], + T![preserves_flags], + T![noreturn], + T![nostack], + T![may_unwind], + T![att_syntax], + T![raw], + ]; + let m = p.start(); + if !OPTIONS.iter().any(|&syntax| p.eat_contextual_kw(syntax)) { + p.err_and_bump("expected asm option"); + m.abandon(p); + continue; + } + m.complete(p, ASM_OPTION); + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_clobber_abi(p: &mut Parser<'_>) { + p.expect(T!['(']); + + while !p.eat(T![')']) && !p.at(EOF) { + if !p.expect(T![string]) { + break; + } + + // Allow trailing commas + if p.eat(T![')']) { + break; + } + p.expect(T![,]); + } +} + +fn parse_reg(p: &mut Parser<'_>) { + p.expect(T!['(']); + if p.at(T![ident]) { + let m = p.start(); + name_ref(p); + m.complete(p, ASM_REG_SPEC); + } else if p.at(T![string]) { + let m = p.start(); + p.bump_any(); + m.complete(p, ASM_REG_SPEC); + } else { + p.err_and_bump("expected register name"); + } + p.expect(T![')']); +} + // test array_expr // fn foo() { // []; diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs index 3590486bd29e..dceac815e0b5 100644 --- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs +++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs @@ -202,9 +202,7 @@ impl<'a> Converter<'a> { err = "Unknown lifetime prefix"; LIFETIME_IDENT } - rustc_lexer::TokenKind::RawLifetime => { - LIFETIME_IDENT - } + rustc_lexer::TokenKind::RawLifetime => LIFETIME_IDENT, rustc_lexer::TokenKind::Semi => T![;], rustc_lexer::TokenKind::Comma => T![,], diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs index 7d3eb5de25f9..f6b3783d1cac 100644 --- a/src/tools/rust-analyzer/crates/parser/src/parser.rs +++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs @@ -131,6 +131,14 @@ impl<'t> Parser<'t> { true } + pub(crate) fn eat_contextual_kw(&mut self, kind: SyntaxKind) -> bool { + if !self.at_contextual_kw(kind) { + return false; + } + self.bump_remap(kind); + true + } + fn at_composite2(&self, n: usize, k1: SyntaxKind, k2: SyntaxKind) -> bool { self.inp.kind(self.pos + n) == k1 && self.inp.kind(self.pos + n + 1) == k2 diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs index 00f212487ae6..288a07ef44dc 100644 --- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs +++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs @@ -111,16 +111,32 @@ pub enum SyntaxKind { YIELD_KW, ASM_KW, ASYNC_KW, + ATT_SYNTAX_KW, AUTO_KW, AWAIT_KW, BUILTIN_KW, + CLOBBER_ABI_KW, DEFAULT_KW, DYN_KW, FORMAT_ARGS_KW, GEN_KW, + INLATEOUT_KW, + INOUT_KW, + LABEL_KW, + LATEOUT_KW, MACRO_RULES_KW, + MAY_UNWIND_KW, + NOMEM_KW, + NORETURN_KW, + NOSTACK_KW, OFFSET_OF_KW, + OPTIONS_KW, + OUT_KW, + PRESERVES_FLAGS_KW, + PURE_KW, RAW_KW, + READONLY_KW, + SYM_KW, TRY_KW, UNION_KW, YEET_KW, @@ -146,7 +162,20 @@ pub enum SyntaxKind { ARG_LIST, ARRAY_EXPR, ARRAY_TYPE, + ASM_CLOBBER_ABI, + ASM_CONST, + ASM_DIR_SPEC, ASM_EXPR, + ASM_LABEL, + ASM_OPERAND, + ASM_OPERAND_EXPR, + ASM_OPERAND_NAMED, + ASM_OPTION, + ASM_OPTIONS, + ASM_PIECE, + ASM_REG_OPERAND, + ASM_REG_SPEC, + ASM_SYM, ASSOC_ITEM, ASSOC_ITEM_LIST, ASSOC_TYPE_ARG, @@ -364,14 +393,30 @@ impl SyntaxKind { pub fn is_contextual_keyword(self, edition: Edition) -> bool { match self { ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -435,14 +480,30 @@ impl SyntaxKind { GEN_KW if Edition::Edition2024 <= edition => true, TRY_KW if Edition::Edition2018 <= edition => true, ASM_KW => true, + ATT_SYNTAX_KW => true, AUTO_KW => true, BUILTIN_KW => true, + CLOBBER_ABI_KW => true, DEFAULT_KW => true, DYN_KW if edition < Edition::Edition2018 => true, FORMAT_ARGS_KW => true, + INLATEOUT_KW => true, + INOUT_KW => true, + LABEL_KW => true, + LATEOUT_KW => true, MACRO_RULES_KW => true, + MAY_UNWIND_KW => true, + NOMEM_KW => true, + NORETURN_KW => true, + NOSTACK_KW => true, OFFSET_OF_KW => true, + OPTIONS_KW => true, + OUT_KW => true, + PRESERVES_FLAGS_KW => true, + PURE_KW => true, RAW_KW => true, + READONLY_KW => true, + SYM_KW => true, UNION_KW => true, YEET_KW => true, _ => false, @@ -580,14 +641,30 @@ impl SyntaxKind { pub fn from_contextual_keyword(ident: &str, edition: Edition) -> Option { let kw = match ident { "asm" => ASM_KW, + "att_syntax" => ATT_SYNTAX_KW, "auto" => AUTO_KW, "builtin" => BUILTIN_KW, + "clobber_abi" => CLOBBER_ABI_KW, "default" => DEFAULT_KW, "dyn" if edition < Edition::Edition2018 => DYN_KW, "format_args" => FORMAT_ARGS_KW, + "inlateout" => INLATEOUT_KW, + "inout" => INOUT_KW, + "label" => LABEL_KW, + "lateout" => LATEOUT_KW, "macro_rules" => MACRO_RULES_KW, + "may_unwind" => MAY_UNWIND_KW, + "nomem" => NOMEM_KW, + "noreturn" => NORETURN_KW, + "nostack" => NOSTACK_KW, "offset_of" => OFFSET_OF_KW, + "options" => OPTIONS_KW, + "out" => OUT_KW, + "preserves_flags" => PRESERVES_FLAGS_KW, + "pure" => PURE_KW, "raw" => RAW_KW, + "readonly" => READONLY_KW, + "sym" => SYM_KW, "union" => UNION_KW, "yeet" => YEET_KW, _ => return None, @@ -630,4 +707,4 @@ impl SyntaxKind { } } #[macro_export] -macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } +macro_rules ! T { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs index 9ce5a2ae748f..164d0f36f1be 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs @@ -19,6 +19,8 @@ mod ok { #[test] fn as_precedence() { run_and_expect_no_errors("test_data/parser/inline/ok/as_precedence.rs"); } #[test] + fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); } + #[test] fn assoc_const_eq() { run_and_expect_no_errors("test_data/parser/inline/ok/assoc_const_eq.rs"); } diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast new file mode 100644 index 000000000000..f0213d0b5e36 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rast @@ -0,0 +1,85 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "foo" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + EXPR_STMT + ASM_EXPR + BUILTIN_KW "builtin" + POUND "#" + ASM_KW "asm" + L_PAREN "(" + WHITESPACE "\n " + LITERAL + STRING "\"mov {tmp}, {x}\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {tmp}, 1\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"shl {x}, 2\"" + COMMA "," + WHITESPACE "\n " + LITERAL + STRING "\"add {x}, {tmp}\"" + COMMA "," + WHITESPACE "\n " + ASM_OPERAND_NAMED + NAME + IDENT "x" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + ASM_DIR_SPEC + INOUT_KW "inout" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + ASM_OPERAND_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + COMMA "," + WHITESPACE "\n " + ASM_OPERAND_NAMED + NAME + IDENT "tmp" + WHITESPACE " " + EQ "=" + WHITESPACE " " + ASM_REG_OPERAND + ASM_DIR_SPEC + OUT_KW "out" + L_PAREN "(" + ASM_REG_SPEC + NAME_REF + IDENT "reg" + R_PAREN ")" + WHITESPACE " " + ASM_OPERAND_EXPR + UNDERSCORE_EXPR + UNDERSCORE "_" + COMMA "," + WHITESPACE "\n " + R_PAREN ")" + SEMICOLON ";" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs new file mode 100644 index 000000000000..0906cd3e7197 --- /dev/null +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_expr.rs @@ -0,0 +1,10 @@ +fn foo() { + builtin#asm( + "mov {tmp}, {x}", + "shl {tmp}, 1", + "shl {x}, 2", + "add {x}, {tmp}", + x = inout(reg) x, + tmp = out(reg) _, + ); +} diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 361900b6d3e4..19a84ac54096 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rast @@ -19,7 +19,7 @@ SOURCE_FILE ASM_KW "asm" L_PAREN "(" LITERAL - INT_NUMBER "0" + STRING "\"\"" R_PAREN ")" SEMICOLON ";" WHITESPACE "\n " diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 14431b0210ea..920d0f794f24 100644 --- a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/builtin_expr.rs @@ -1,5 +1,5 @@ fn foo() { - builtin#asm(0); + builtin#asm(""); builtin#format_args("", 0, 1, a = 2 + 3, a + b); builtin#offset_of(Foo, bar.baz.0); } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs index d50a3cdbf72d..011baad65f7d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs @@ -152,10 +152,9 @@ impl ProcMacro { def_site: Span, call_site: Span, mixed_site: Span, + current_dir: Option, ) -> Result, PanicMessage>, ServerError> { let version = self.process.version(); - let current_dir = - env.get("CARGO_RUSTC_CURRENT_DIR").or_else(|| env.get("CARGO_MANIFEST_DIR")); let mut span_data_table = SpanDataIndexMap::default(); let def_site = span_data_table.insert_full(def_site).0; diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs index b8ac55ed0d5d..91bdef4889ca 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs @@ -52,6 +52,15 @@ pub use crate::{ }; pub use cargo_metadata::Metadata; +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ProjectJsonFromCommand { + /// The data describing this project, such as its dependencies. + pub data: ProjectJsonData, + /// The build system specific file that describes this project, + /// such as a `my-project/BUCK` file. + pub buildfile: AbsPathBuf, +} + #[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] pub enum ProjectManifest { ProjectJson(ManifestPath), diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index a09c7a77abce..6a88cf022dfb 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -66,6 +66,8 @@ pub struct ProjectJson { /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, + /// The path to the rust-project.json file. May be None if this + /// data was generated by the discoverConfig command. manifest: Option, crates: Vec, /// Configuration for CLI commands. diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs index f540bb94c196..30d1ddb636ef 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs @@ -45,39 +45,6 @@ fn load_cargo_with_overrides( to_crate_graph(project_workspace) } -fn load_cargo_with_fake_sysroot( - file_map: &mut FxHashMap, - file: &str, -) -> (CrateGraph, ProcMacroPaths) { - let meta: Metadata = get_test_json_file(file); - let manifest_path = - ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - let project_workspace = ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), - rustc: Err(None), - cargo_config_extra_env: Default::default(), - error: None, - }, - sysroot: get_fake_sysroot(), - rustc_cfg: Vec::new(), - cfg_overrides: Default::default(), - toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), - }; - project_workspace.to_crate_graph( - &mut { - |path| { - let len = file_map.len(); - Some(*file_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - } - }, - &Default::default(), - ) -} - fn load_rust_project(file: &str) -> (CrateGraph, ProcMacroPaths) { let data = get_test_json_file(file); let project = rooted_project_json(data); @@ -253,34 +220,6 @@ fn rust_project_is_proc_macro_has_proc_macro_dep() { crate_data.dependencies.iter().find(|&dep| dep.name.deref() == "proc_macro").unwrap(); } -#[test] -fn crate_graph_dedup_identical() { - let (mut crate_graph, proc_macros) = - load_cargo_with_fake_sysroot(&mut Default::default(), "regex-metadata.json"); - crate_graph.sort_deps(); - - let (d_crate_graph, mut d_proc_macros) = (crate_graph.clone(), proc_macros.clone()); - - crate_graph.extend(d_crate_graph.clone(), &mut d_proc_macros, |(_, a), (_, b)| a == b); - assert!(crate_graph.iter().eq(d_crate_graph.iter())); - assert_eq!(proc_macros, d_proc_macros); -} - -#[test] -fn crate_graph_dedup() { - let path_map = &mut Default::default(); - let (mut crate_graph, _proc_macros) = - load_cargo_with_fake_sysroot(path_map, "ripgrep-metadata.json"); - assert_eq!(crate_graph.iter().count(), 81); - crate_graph.sort_deps(); - let (regex_crate_graph, mut regex_proc_macros) = - load_cargo_with_fake_sysroot(path_map, "regex-metadata.json"); - assert_eq!(regex_crate_graph.iter().count(), 60); - - crate_graph.extend(regex_crate_graph, &mut regex_proc_macros, |(_, a), (_, b)| a == b); - assert_eq!(crate_graph.iter().count(), 118); -} - #[test] // FIXME Remove the ignore #[ignore = "requires nightly until the sysroot ships a cargo workspace for library on stable"] diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs index 7834238acefd..17b40a87cda9 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs @@ -109,7 +109,7 @@ impl fmt::Debug for ProjectWorkspace { ProjectWorkspaceKind::Cargo { cargo, error: _, - build_scripts: _, + build_scripts, rustc, cargo_config_extra_env, } => f @@ -126,6 +126,7 @@ impl fmt::Debug for ProjectWorkspace { .field("toolchain", &toolchain) .field("data_layout", &target_layout) .field("cargo_config_extra_env", &cargo_config_extra_env) + .field("build_scripts", &build_scripts.error().unwrap_or("ok")) .finish(), ProjectWorkspaceKind::Json(project) => { let mut debug_struct = f.debug_struct("Json"); @@ -1456,7 +1457,7 @@ fn sysroot_to_crate_graph( // Remove all crates except the ones we are interested in to keep the sysroot graph small. let removed_mapping = cg.remove_crates_except(&marker_set); - let mapping = crate_graph.extend(cg, &mut pm, |(_, a), (_, b)| a == b); + let mapping = crate_graph.extend(cg, &mut pm); // Map the id through the removal mapping first, then through the crate graph extension mapping. pub_deps.iter_mut().for_each(|(_, cid, _)| { @@ -1554,6 +1555,6 @@ fn add_proc_macro_dep(crate_graph: &mut CrateGraph, from: CrateId, to: CrateId, fn add_dep_inner(graph: &mut CrateGraph, from: CrateId, dep: Dependency) { if let Err(err) = graph.add_dep(from, dep) { - tracing::error!("{}", err) + tracing::warn!("{}", err) } } diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json deleted file mode 100644 index 371464dd20aa..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/test_data/regex-metadata.json +++ /dev/null @@ -1,6420 +0,0 @@ -{ - "packages": [ - { - "name": "aho-corasick", - "version": "0.7.20", - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [ - "memchr/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "aho", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cc", - "version": "1.0.79", - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jobserver", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "gcc-shim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cc_env", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cxxflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "jobserver": [ - "dep:jobserver" - ], - "parallel": [ - "jobserver" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [ - "development-tools::build-utils" - ], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/cc-rs", - "homepage": "https://github.com/rust-lang/cc-rs", - "documentation": "https://docs.rs/cc", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "0.1.10", - "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "1.0.0", - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "docopt", - "version": "1.1.1", - "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Command line argument parsing.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std", - "unicode" - ], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "strsim", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "docopt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "docopt-wordlist", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/src/wordlist.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cargo", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cargo.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cp", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/cp.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "decode", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/decode.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "hashmap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/hashmap.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "optional_command", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/optional_command.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "verbose_multiple", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/examples/verbose_multiple.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/docopt-1.1.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "command-line-interface" - ], - "keywords": [ - "docopt", - "argument", - "command", - "argv" - ], - "readme": "README.md", - "repository": "https://github.com/docopt/docopt.rs", - "homepage": "https://github.com/docopt/docopt.rs", - "documentation": "http://burntsushi.net/rustdoc/docopt/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "getrandom", - "version": "0.2.9", - "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A small cross-platform library for retrieving random data from system source", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "js-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasm-bindgen", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.62", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasm-bindgen-test", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.18", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\"))", - "registry": null - }, - { - "name": "wasi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(target_os = \"wasi\")", - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.139", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "getrandom", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "custom", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/custom.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "normal", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/normal.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "rdrand", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/tests/rdrand.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/benches/buffer.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "custom": [], - "js": [ - "wasm-bindgen", - "js-sys" - ], - "js-sys": [ - "dep:js-sys" - ], - "rdrand": [], - "rustc-dep-of-std": [ - "compiler_builtins", - "core", - "libc/rustc-dep-of-std", - "wasi/rustc-dep-of-std" - ], - "std": [], - "test-in-browser": [], - "wasm-bindgen": [ - "dep:wasm-bindgen" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/getrandom-0.2.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "std", - "custom" - ], - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers" - ], - "categories": [ - "os", - "no-std" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-random/getrandom", - "homepage": null, - "documentation": "https://docs.rs/getrandom", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "lazy_static", - "version": "1.4.0", - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro for declaring lazily evaluated statics in Rust.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "spin", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "no_std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "spin": [ - "dep:spin" - ], - "spin_no_std": [ - "spin" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Marvin Löbel " - ], - "categories": [ - "no-std", - "rust-patterns", - "memory-management" - ], - "keywords": [ - "macro", - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", - "homepage": null, - "documentation": "https://docs.rs/lazy_static", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "libc", - "version": "0.2.142", - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings to platform libraries like libc.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "libc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "const_fn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "align": [], - "const-extern-fn": [], - "default": [ - "std" - ], - "extra_traits": [], - "rustc-dep-of-std": [ - "align", - "rustc-std-workspace-core" - ], - "rustc-std-workspace-core": [ - "dep:rustc-std-workspace-core" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "const-extern-fn", - "extra_traits" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os" - ], - "keywords": [ - "libc", - "ffi", - "bindings", - "operating", - "system" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/libc", - "homepage": "https://github.com/rust-lang/libc", - "documentation": "https://docs.rs/libc/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memchr", - "version": "2.5.0", - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Safe interface to memchr.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memchr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "libc": [ - "dep:libc" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant ", - "bluss" - ], - "categories": [], - "keywords": [ - "memchr", - "char", - "scan", - "strchr", - "string" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/memchr", - "homepage": "https://github.com/BurntSushi/memchr", - "documentation": "https://docs.rs/memchr/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memmap", - "version": "0.6.2", - "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Cross-platform Rust API for memory-mapped file IO", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "tempdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - }, - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "basetsd", - "handleapi", - "memoryapi", - "minwindef", - "std", - "sysinfoapi" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memmap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/examples/cat.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap-0.6.2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Dan Burkert " - ], - "categories": [], - "keywords": [ - "mmap", - "memory-map", - "io", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/danburkert/memmap-rs", - "homepage": null, - "documentation": "https://docs.rs/memmap", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pkg-config", - "version": "0.3.26", - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pkg-config", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/pkg-config-rs", - "homepage": null, - "documentation": "https://docs.rs/pkg-config", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "proc-macro2", - "version": "1.0.56", - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "proc-macro2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "comments", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "features", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "marker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_fmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "nightly": [], - "proc-macro": [], - "span-locations": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustc-args": [ - "--cfg", - "procmacro2_semver_exempt" - ], - "rustdoc-args": [ - "--cfg", - "procmacro2_semver_exempt", - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "span-locations" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay ", - "Alex Crichton " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/proc-macro2", - "homepage": null, - "documentation": "https://docs.rs/proc-macro2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "quickcheck", - "version": "1.0.3", - "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Automatic property based testing with shrinking.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "env_logger", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quickcheck", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "btree_set_range", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/btree_set_range.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "out_of_bounds", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/out_of_bounds.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reverse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reverse_single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/reverse_single.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "sieve", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sieve.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "sort", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/examples/sort.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "regex", - "use_logging" - ], - "env_logger": [ - "dep:env_logger" - ], - "log": [ - "dep:log" - ], - "regex": [ - "env_logger/regex" - ], - "use_logging": [ - "log", - "env_logger" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quickcheck-1.0.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "development-tools::testing" - ], - "keywords": [ - "testing", - "quickcheck", - "property", - "shrinking", - "fuzz" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/quickcheck", - "homepage": "https://github.com/BurntSushi/quickcheck", - "documentation": "https://docs.rs/quickcheck", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "quote", - "version": "1.0.26", - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Quasi-quoting macro quote!(...)", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.52", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.66", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quote", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "proc-macro": [ - "proc-macro2/proc-macro" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/quote", - "homepage": null, - "documentation": "https://docs.rs/quote/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "rand", - "version": "0.8.5", - "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Random number generators and other randomness functionality.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.4", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.7", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [ - "into_bits" - ], - "target": null, - "registry": null - }, - { - "name": "rand_chacha", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.103", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "bincode", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_pcg", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.22", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "rand", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [ - "rand_core/alloc" - ], - "default": [ - "std", - "std_rng" - ], - "getrandom": [ - "rand_core/getrandom" - ], - "libc": [ - "dep:libc" - ], - "log": [ - "dep:log" - ], - "min_const_gen": [], - "nightly": [], - "packed_simd": [ - "dep:packed_simd" - ], - "rand_chacha": [ - "dep:rand_chacha" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde", - "rand_core/serde1" - ], - "simd_support": [ - "packed_simd" - ], - "small_rng": [], - "std": [ - "rand_core/std", - "rand_chacha/std", - "alloc", - "getrandom", - "libc" - ], - "std_rng": [ - "rand_chacha" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand-0.8.5/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ] - } - }, - "playground": { - "features": [ - "small_rng", - "serde1" - ] - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers", - "The Rust Project Developers" - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [ - "random", - "rng" - ], - "readme": "README.md", - "repository": "https://github.com/rust-random/rand", - "homepage": "https://rust-random.github.io/book", - "documentation": "https://docs.rs/rand", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "rand_core", - "version": "0.6.4", - "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Core random number generator traits and tools for implementation.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "getrandom", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "rand_core", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [], - "getrandom": [ - "dep:getrandom" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde" - ], - "std": [ - "alloc", - "getrandom", - "getrandom/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/rand_core-0.6.4/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ] - } - }, - "playground": { - "all-features": true - } - }, - "publish": null, - "authors": [ - "The Rand Project Developers", - "The Rust Project Developers" - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [ - "random", - "rng" - ], - "readme": "README.md", - "repository": "https://github.com/rust-random/rand", - "homepage": "https://rust-random.github.io/book", - "documentation": "https://docs.rs/rand_core", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex", - "version": "1.7.1", - "id": "regex 1.7.1 (path+file:///$ROOT$regex)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6.27", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$regex/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-cheat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-replace.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$regex/examples/shootout-regex-dna-single.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$regex/examples/shootout-regex-dna.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$regex/tests/test_default.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$regex/tests/test_default_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$regex/tests/test_nfa.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$regex/tests/test_nfa_utf8bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$regex/tests/test_nfa_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$regex/tests/test_backtrack.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$regex/tests/test_backtrack_utf8bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$regex/tests/test_backtrack_bytes.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$regex/tests/test_crates_regex.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$regex/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex", - "version": "1.8.1", - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "regex-benchmark", - "version": "0.1.0", - "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Regex benchmarks for Rust's and other engines.", - "source": null, - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "docopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libpcre-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memmap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "onig", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "pkg-config", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.9", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "regex-run-one", - "src_path": "$ROOT$regex/bench/src/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$regex/bench/src/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$regex/bench/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "libpcre-sys": [ - "dep:libpcre-sys" - ], - "onig": [ - "dep:onig" - ], - "re-onig": [ - "onig" - ], - "re-pcre1": [ - "libpcre-sys" - ], - "re-pcre2": [], - "re-re2": [], - "re-rust": [], - "re-rust-bytes": [], - "re-tcl": [] - }, - "manifest_path": "$ROOT$regex/bench/Cargo.toml", - "metadata": null, - "publish": [], - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": null, - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-debug", - "version": "0.1.0", - "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A tool useful for debugging regular expressions.", - "source": null, - "dependencies": [ - { - "name": "docopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - }, - { - "name": "regex-syntax", - "source": null, - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex/regex-syntax" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "regex-debug", - "src_path": "$ROOT$regex/regex-debug/src/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$regex/regex-debug/Cargo.toml", - "metadata": null, - "publish": [], - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": null, - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.6.28", - "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$regex/regex-syntax/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$regex/regex-syntax/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "unicode" - ], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$regex/regex-syntax/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.7.1", - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std", - "unicode" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "rure", - "version": "0.2.2", - "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A C API for Rust's regular expression library.\n", - "source": null, - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": null, - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$regex" - } - ], - "targets": [ - { - "kind": [ - "staticlib", - "cdylib", - "rlib" - ], - "crate_types": [ - "staticlib", - "cdylib", - "rlib" - ], - "name": "rure", - "src_path": "$ROOT$regex/regex-capi/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$regex/regex-capi/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://github.com/rust-lang/regex/tree/master/regex-capi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "serde", - "version": "1.0.160", - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A generic serialization/deserialization framework", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.160", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "derive": [ - "serde_derive" - ], - "rc": [], - "serde_derive": [ - "dep:serde_derive" - ], - "std": [], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "derive" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "derive", - "rc" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://docs.rs/serde", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.19" - }, - { - "name": "serde_derive", - "version": "1.0.160", - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "proc-macro" - ], - "crate_types": [ - "proc-macro" - ], - "name": "serde_derive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [], - "deserialize_in_place": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std", - "derive" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://serde.rs/derive.html", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "strsim", - "version": "0.10.0", - "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Implementations of string similarity metrics. Includes Hamming, Levenshtein,\nOSA, Damerau-Levenshtein, Jaro, Jaro-Winkler, and Sørensen-Dice.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "strsim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lib", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/tests/lib.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benches", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/benches/benches.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.10.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Danny Guo " - ], - "categories": [], - "keywords": [ - "string", - "similarity", - "Hamming", - "Levenshtein", - "Jaro" - ], - "readme": "README.md", - "repository": "https://github.com/dguo/strsim-rs", - "homepage": "https://github.com/dguo/strsim-rs", - "documentation": "https://docs.rs/strsim/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "syn", - "version": "2.0.15", - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Parser for Rust source code", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.55", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.25", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "anyhow", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "flate2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "insta", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rayon", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "reqwest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "blocking" - ], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn-test-suite", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tar", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.16", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "syn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_asyncness", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_attribute", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_derive_input", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_expr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_generics", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_grouping", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_item", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_iterators", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_lit", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_meta", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_pat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_path", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_precedence", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_receiver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_round_trip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_shebang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_should_parse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_stmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_token_trees", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_visibility", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zzz_stable", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "rust", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "clone-impls": [], - "default": [ - "derive", - "parsing", - "printing", - "clone-impls", - "proc-macro" - ], - "derive": [], - "extra-traits": [], - "fold": [], - "full": [], - "parsing": [], - "printing": [ - "quote" - ], - "proc-macro": [ - "proc-macro2/proc-macro", - "quote/proc-macro" - ], - "quote": [ - "dep:quote" - ], - "test": [ - "syn-test-suite/all-features" - ], - "visit": [], - "visit-mut": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "full", - "visit", - "visit-mut", - "fold", - "extra-traits" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "parser-implementations" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/syn", - "homepage": null, - "documentation": "https://docs.rs/syn", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "unicode-ident", - "version": "1.0.8", - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", - "license_file": null, - "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "roaring", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-trie", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-xid", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compare", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "static_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "xid", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "no-std" - ], - "keywords": [ - "unicode", - "xid" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/unicode-ident", - "homepage": null, - "documentation": "https://docs.rs/unicode-ident", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "wasi", - "version": "0.11.0+wasi-snapshot-preview1", - "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT", - "license_file": null, - "description": "Experimental WASI API bindings for Rust", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-alloc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "wasi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "rustc-dep-of-std": [ - "compiler_builtins", - "core", - "rustc-std-workspace-alloc" - ], - "rustc-std-workspace-alloc": [ - "dep:rustc-std-workspace-alloc" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasi-0.11.0+wasi-snapshot-preview1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Cranelift Project Developers" - ], - "categories": [ - "no-std", - "wasm" - ], - "keywords": [ - "webassembly", - "wasm" - ], - "readme": "README.md", - "repository": "https://github.com/bytecodealliance/wasi", - "homepage": null, - "documentation": "https://docs.rs/wasi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi", - "version": "0.3.9", - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings for all of Windows API.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-i686-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "i686-pc-windows-gnu", - "registry": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "x86_64-pc-windows-gnu", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "accctrl": [], - "aclapi": [], - "activation": [], - "adhoc": [], - "appmgmt": [], - "audioclient": [], - "audiosessiontypes": [], - "avrt": [], - "basetsd": [], - "bcrypt": [], - "bits": [], - "bits10_1": [], - "bits1_5": [], - "bits2_0": [], - "bits2_5": [], - "bits3_0": [], - "bits4_0": [], - "bits5_0": [], - "bitscfg": [], - "bitsmsg": [], - "bluetoothapis": [], - "bluetoothleapis": [], - "bthdef": [], - "bthioctl": [], - "bthledef": [], - "bthsdpdef": [], - "bugcodes": [], - "cderr": [], - "cfg": [], - "cfgmgr32": [], - "cguid": [], - "combaseapi": [], - "coml2api": [], - "commapi": [], - "commctrl": [], - "commdlg": [], - "commoncontrols": [], - "consoleapi": [], - "corecrt": [], - "corsym": [], - "d2d1": [], - "d2d1_1": [], - "d2d1_2": [], - "d2d1_3": [], - "d2d1effectauthor": [], - "d2d1effects": [], - "d2d1effects_1": [], - "d2d1effects_2": [], - "d2d1svg": [], - "d2dbasetypes": [], - "d3d": [], - "d3d10": [], - "d3d10_1": [], - "d3d10_1shader": [], - "d3d10effect": [], - "d3d10misc": [], - "d3d10sdklayers": [], - "d3d10shader": [], - "d3d11": [], - "d3d11_1": [], - "d3d11_2": [], - "d3d11_3": [], - "d3d11_4": [], - "d3d11on12": [], - "d3d11sdklayers": [], - "d3d11shader": [], - "d3d11tokenizedprogramformat": [], - "d3d12": [], - "d3d12sdklayers": [], - "d3d12shader": [], - "d3d9": [], - "d3d9caps": [], - "d3d9types": [], - "d3dcommon": [], - "d3dcompiler": [], - "d3dcsx": [], - "d3dkmdt": [], - "d3dkmthk": [], - "d3dukmdt": [], - "d3dx10core": [], - "d3dx10math": [], - "d3dx10mesh": [], - "datetimeapi": [], - "davclnt": [], - "dbghelp": [], - "dbt": [], - "dcommon": [], - "dcomp": [], - "dcompanimation": [], - "dcomptypes": [], - "dde": [], - "ddraw": [], - "ddrawi": [], - "ddrawint": [], - "debug": [ - "impl-debug" - ], - "debugapi": [], - "devguid": [], - "devicetopology": [], - "devpkey": [], - "devpropdef": [], - "dinput": [], - "dinputd": [], - "dispex": [], - "dmksctl": [], - "dmusicc": [], - "docobj": [], - "documenttarget": [], - "dot1x": [], - "dpa_dsa": [], - "dpapi": [], - "dsgetdc": [], - "dsound": [], - "dsrole": [], - "dvp": [], - "dwmapi": [], - "dwrite": [], - "dwrite_1": [], - "dwrite_2": [], - "dwrite_3": [], - "dxdiag": [], - "dxfile": [], - "dxgi": [], - "dxgi1_2": [], - "dxgi1_3": [], - "dxgi1_4": [], - "dxgi1_5": [], - "dxgi1_6": [], - "dxgidebug": [], - "dxgiformat": [], - "dxgitype": [], - "dxva2api": [], - "dxvahd": [], - "eaptypes": [], - "enclaveapi": [], - "endpointvolume": [], - "errhandlingapi": [], - "everything": [], - "evntcons": [], - "evntprov": [], - "evntrace": [], - "excpt": [], - "exdisp": [], - "fibersapi": [], - "fileapi": [], - "functiondiscoverykeys_devpkey": [], - "gl-gl": [], - "guiddef": [], - "handleapi": [], - "heapapi": [], - "hidclass": [], - "hidpi": [], - "hidsdi": [], - "hidusage": [], - "highlevelmonitorconfigurationapi": [], - "hstring": [], - "http": [], - "ifdef": [], - "ifmib": [], - "imm": [], - "impl-debug": [], - "impl-default": [], - "in6addr": [], - "inaddr": [], - "inspectable": [], - "interlockedapi": [], - "intsafe": [], - "ioapiset": [], - "ipexport": [], - "iphlpapi": [], - "ipifcons": [], - "ipmib": [], - "iprtrmib": [], - "iptypes": [], - "jobapi": [], - "jobapi2": [], - "knownfolders": [], - "ks": [], - "ksmedia": [], - "ktmtypes": [], - "ktmw32": [], - "l2cmn": [], - "libloaderapi": [], - "limits": [], - "lmaccess": [], - "lmalert": [], - "lmapibuf": [], - "lmat": [], - "lmcons": [], - "lmdfs": [], - "lmerrlog": [], - "lmjoin": [], - "lmmsg": [], - "lmremutl": [], - "lmrepl": [], - "lmserver": [], - "lmshare": [], - "lmstats": [], - "lmsvc": [], - "lmuse": [], - "lmwksta": [], - "lowlevelmonitorconfigurationapi": [], - "lsalookup": [], - "memoryapi": [], - "minschannel": [], - "minwinbase": [], - "minwindef": [], - "mmdeviceapi": [], - "mmeapi": [], - "mmreg": [], - "mmsystem": [], - "mprapidef": [], - "msaatext": [], - "mscat": [], - "mschapp": [], - "mssip": [], - "mstcpip": [], - "mswsock": [], - "mswsockdef": [], - "namedpipeapi": [], - "namespaceapi": [], - "nb30": [], - "ncrypt": [], - "netioapi": [], - "nldef": [], - "ntddndis": [], - "ntddscsi": [], - "ntddser": [], - "ntdef": [], - "ntlsa": [], - "ntsecapi": [], - "ntstatus": [], - "oaidl": [], - "objbase": [], - "objidl": [], - "objidlbase": [], - "ocidl": [], - "ole2": [], - "oleauto": [], - "olectl": [], - "oleidl": [], - "opmapi": [], - "pdh": [], - "perflib": [], - "physicalmonitorenumerationapi": [], - "playsoundapi": [], - "portabledevice": [], - "portabledeviceapi": [], - "portabledevicetypes": [], - "powerbase": [], - "powersetting": [], - "powrprof": [], - "processenv": [], - "processsnapshot": [], - "processthreadsapi": [], - "processtopologyapi": [], - "profileapi": [], - "propidl": [], - "propkey": [], - "propkeydef": [], - "propsys": [], - "prsht": [], - "psapi": [], - "qos": [], - "realtimeapiset": [], - "reason": [], - "restartmanager": [], - "restrictederrorinfo": [], - "rmxfguid": [], - "roapi": [], - "robuffer": [], - "roerrorapi": [], - "rpc": [], - "rpcdce": [], - "rpcndr": [], - "rtinfo": [], - "sapi": [], - "sapi51": [], - "sapi53": [], - "sapiddk": [], - "sapiddk51": [], - "schannel": [], - "sddl": [], - "securityappcontainer": [], - "securitybaseapi": [], - "servprov": [], - "setupapi": [], - "shellapi": [], - "shellscalingapi": [], - "shlobj": [], - "shobjidl": [], - "shobjidl_core": [], - "shtypes": [], - "softpub": [], - "spapidef": [], - "spellcheck": [], - "sporder": [], - "sql": [], - "sqlext": [], - "sqltypes": [], - "sqlucode": [], - "sspi": [], - "std": [], - "stralign": [], - "stringapiset": [], - "strmif": [], - "subauth": [], - "synchapi": [], - "sysinfoapi": [], - "systemtopologyapi": [], - "taskschd": [], - "tcpestats": [], - "tcpmib": [], - "textstor": [], - "threadpoolapiset": [], - "threadpoollegacyapiset": [], - "timeapi": [], - "timezoneapi": [], - "tlhelp32": [], - "transportsettingcommon": [], - "tvout": [], - "udpmib": [], - "unknwnbase": [], - "urlhist": [], - "urlmon": [], - "usb": [], - "usbioctl": [], - "usbiodef": [], - "usbscan": [], - "usbspec": [], - "userenv": [], - "usp10": [], - "utilapiset": [], - "uxtheme": [], - "vadefs": [], - "vcruntime": [], - "vsbackup": [], - "vss": [], - "vsserror": [], - "vswriter": [], - "wbemads": [], - "wbemcli": [], - "wbemdisp": [], - "wbemprov": [], - "wbemtran": [], - "wct": [], - "werapi": [], - "winbase": [], - "wincodec": [], - "wincodecsdk": [], - "wincon": [], - "wincontypes": [], - "wincred": [], - "wincrypt": [], - "windef": [], - "windot11": [], - "windowsceip": [], - "windowsx": [], - "winefs": [], - "winerror": [], - "winevt": [], - "wingdi": [], - "winhttp": [], - "wininet": [], - "winineti": [], - "winioctl": [], - "winnetwk": [], - "winnls": [], - "winnt": [], - "winreg": [], - "winsafer": [], - "winscard": [], - "winsmcrd": [], - "winsock2": [], - "winspool": [], - "winstring": [], - "winsvc": [], - "wintrust": [], - "winusb": [], - "winusbio": [], - "winuser": [], - "winver": [], - "wlanapi": [], - "wlanihv": [], - "wlanihvtypes": [], - "wlantypes": [], - "wlclient": [], - "wmistr": [], - "wnnc": [], - "wow64apiset": [], - "wpdmtpextensions": [], - "ws2bth": [], - "ws2def": [], - "ws2ipdef": [], - "ws2spi": [], - "ws2tcpip": [], - "wtsapi32": [], - "wtypes": [], - "wtypesbase": [], - "xinput": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-pc-windows-msvc", - "features": [ - "everything", - "impl-debug", - "impl-default" - ], - "targets": [ - "aarch64-pc-windows-msvc", - "i686-pc-windows-msvc", - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os::windows-apis" - ], - "keywords": [ - "windows", - "ffi", - "win32", - "com", - "directx" - ], - "readme": "README.md", - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": "https://docs.rs/winapi/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-i686-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-i686-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-x86_64-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)" - ], - "resolve": { - "nodes": [ - { - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "strsim", - "pkg": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "wasi", - "pkg": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(target_os = \"wasi\")" - } - ] - } - ], - "features": [] - }, - { - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "rand", - "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "rand_core", - "pkg": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "getrandom", - "small_rng" - ] - }, - { - "id": "rand_core 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "getrandom", - "pkg": "getrandom 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "getrandom" - ] - }, - { - "id": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quickcheck", - "pkg": "quickcheck 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "rand", - "pkg": "rand 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "aho-corasick", - "default", - "memchr", - "perf", - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-benchmark 0.1.0 (path+file:///$ROOT$regex/bench)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "cfg_if", - "pkg": "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "docopt", - "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memmap", - "pkg": "memmap 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pkg_config", - "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "regex-debug 0.1.0 (path+file:///$ROOT$regex/regex-debug)", - "dependencies": [ - "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)", - "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "docopt", - "pkg": "docopt 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "regex-syntax 0.6.28 (path+file:///$ROOT$regex/regex-syntax)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "rure 0.2.2 (path+file:///$ROOT$regex/regex-capi)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.7.1 (path+file:///$ROOT$regex)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.7.1 (path+file:///$ROOT$regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "derive", - "serde_derive", - "std" - ] - }, - { - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "syn", - "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "strsim 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "clone-impls", - "default", - "derive", - "parsing", - "printing", - "proc-macro", - "quote" - ] - }, - { - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "wasi 0.11.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_i686_pc_windows_gnu", - "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "i686-pc-windows-gnu" - } - ] - }, - { - "name": "winapi_x86_64_pc_windows_gnu", - "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "x86_64-pc-windows-gnu" - } - ] - } - ], - "features": [ - "basetsd", - "handleapi", - "memoryapi", - "minwindef", - "std", - "sysinfoapi" - ] - }, - { - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "regex 1.7.1 (path+file:///$ROOT$regex)" - }, - "target_directory": "$ROOT$regex/target", - "version": 1, - "workspace_root": "$ROOT$regex", - "metadata": null -} diff --git a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json b/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json deleted file mode 100644 index 131ff5dd7157..000000000000 --- a/src/tools/rust-analyzer/crates/project-model/test_data/ripgrep-metadata.json +++ /dev/null @@ -1,12816 +0,0 @@ -{ - "packages": [ - { - "name": "aho-corasick", - "version": "0.7.20", - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [ - "memchr/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-0.7.20/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "aho", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "aho-corasick", - "version": "1.0.1", - "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast multiple substring searching.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.17", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "aho_corasick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std", - "perf-literal" - ], - "logging": [ - "dep:log" - ], - "perf-literal": [ - "dep:memchr" - ], - "std": [ - "memchr?/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/aho-corasick-1.0.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "string", - "search", - "text", - "pattern", - "multi" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/aho-corasick", - "homepage": "https://github.com/BurntSushi/aho-corasick", - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "atty", - "version": "0.2.14", - "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "A simple interface for querying atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "hermit-abi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(target_os = \"hermit\")", - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": "cfg(unix)", - "registry": null - }, - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "consoleapi", - "processenv", - "minwinbase", - "minwindef", - "winbase" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "atty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "atty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/examples/atty.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/atty-0.2.14/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "softprops " - ], - "categories": [], - "keywords": [ - "terminal", - "tty", - "isatty" - ], - "readme": "README.md", - "repository": "https://github.com/softprops/atty", - "homepage": "https://github.com/softprops/atty", - "documentation": "http://softprops.github.io/atty", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "base64", - "version": "0.20.0", - "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "encodes and decodes base64 as bytes or utf8", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.5", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "rstest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.12.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rstest_reuse", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "structopt", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.26", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "base64", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "base64", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/examples/base64.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "encode", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/encode.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "tests", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/tests/tests.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benchmarks", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/benches/benchmarks.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/base64-0.20.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alice Maz ", - "Marshall Pierce " - ], - "categories": [ - "encoding" - ], - "keywords": [ - "base64", - "utf8", - "encode", - "decode", - "no_std" - ], - "readme": "README.md", - "repository": "https://github.com/marshallpierce/rust-base64", - "homepage": null, - "documentation": "https://docs.rs/base64", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.57.0" - }, - { - "name": "bitflags", - "version": "1.3.2", - "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to generate structures which behave like bitflags.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bitflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "basic", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/basic.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compile", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/tests/compile.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "example_generated": [], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bitflags-1.3.2/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "example_generated" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "no-std" - ], - "keywords": [ - "bit", - "bitmask", - "bitflags", - "flags" - ], - "readme": "README.md", - "repository": "https://github.com/bitflags/bitflags", - "homepage": "https://github.com/bitflags/bitflags", - "documentation": "https://docs.rs/bitflags", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "bstr", - "version": "1.4.0", - "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A string type that is not required to be valid UTF-8.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.4.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "once_cell", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.14.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-automata", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.85", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-parse", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-segmentation", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bstr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "graphemes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lines", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "uppercase", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "words", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words.rs", - "edition": "2021", - "required-features": [ - "std", - "unicode" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "graphemes-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/graphemes-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lines-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/lines-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "uppercase-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/uppercase-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "words-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/examples/words-std.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [ - "serde?/alloc" - ], - "default": [ - "std", - "unicode" - ], - "serde": [ - "dep:serde" - ], - "std": [ - "alloc", - "memchr/std", - "serde?/std" - ], - "unicode": [ - "dep:once_cell", - "dep:regex-automata" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bstr-1.4.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing", - "encoding" - ], - "keywords": [ - "string", - "str", - "byte", - "bytes", - "text" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/bstr", - "homepage": "https://github.com/BurntSushi/bstr", - "documentation": "https://docs.rs/bstr", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60" - }, - { - "name": "bytecount", - "version": "0.6.3", - "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0/MIT", - "license_file": null, - "description": "count occurrences of a given byte, or the number of UTF-8 code points, in a byte slice, fast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.8", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "bytecount", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "check", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/tests/check.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "generic-simd": [ - "packed_simd" - ], - "html_report": [], - "packed_simd": [ - "dep:packed_simd" - ], - "runtime-dispatch-simd": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/bytecount-0.6.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andre Bogus ", - "Joshua Landau " - ], - "categories": [ - "algorithms", - "no-std" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/llogiq/bytecount", - "homepage": null, - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cc", - "version": "1.0.79", - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A build-time dependency for Cargo build scripts to assist in invoking the native\nC compiler to compile native C code into a static archive to be linked into Rust\ncode.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jobserver", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "gcc-shim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/src/bin/gcc-shim.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cc_env", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cc_env.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cxxflags", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/cxxflags.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "jobserver": [ - "dep:jobserver" - ], - "parallel": [ - "jobserver" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cc-1.0.79/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [ - "development-tools::build-utils" - ], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/cc-rs", - "homepage": "https://github.com/rust-lang/cc-rs", - "documentation": "https://docs.rs/cc", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "cfg-if", - "version": "1.0.0", - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro to ergonomically define an item depending on a large number of #[cfg]\nparameters. Structured like an if-else chain, the first matching branch is the\nitem that gets emitted.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "cfg-if", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "xcrate", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/tests/xcrate.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/cfg-if-1.0.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/cfg-if", - "homepage": "https://github.com/alexcrichton/cfg-if", - "documentation": "https://docs.rs/cfg-if", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "clap", - "version": "2.34.0", - "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "A simple to use, efficient, and full-featured Command Line Argument Parser\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bitflags", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clippy", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "~0.0.166", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "strsim", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "term_size", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "textwrap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-width", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "vec_map", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "yaml-rust", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "version-sync", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ansi_term", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.12", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(not(windows))", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "clap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "ansi_term": [ - "dep:ansi_term" - ], - "atty": [ - "dep:atty" - ], - "clippy": [ - "dep:clippy" - ], - "color": [ - "ansi_term", - "atty" - ], - "debug": [], - "default": [ - "suggestions", - "color", - "vec_map" - ], - "doc": [ - "yaml" - ], - "nightly": [], - "no_cargo": [], - "strsim": [ - "dep:strsim" - ], - "suggestions": [ - "strsim" - ], - "term_size": [ - "dep:term_size" - ], - "unstable": [], - "vec_map": [ - "dep:vec_map" - ], - "wrap_help": [ - "term_size", - "textwrap/term_size" - ], - "yaml": [ - "yaml-rust" - ], - "yaml-rust": [ - "dep:yaml-rust" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/clap-2.34.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "doc" - ] - } - } - }, - "publish": null, - "authors": [ - "Kevin K. " - ], - "categories": [ - "command-line-interface" - ], - "keywords": [ - "argument", - "cli", - "arg", - "parser", - "parse" - ], - "readme": "README.md", - "repository": "https://github.com/clap-rs/clap", - "homepage": "https://clap.rs/", - "documentation": "https://docs.rs/clap/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "crossbeam-channel", - "version": "0.5.8", - "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Multi-producer multi-consumer channels for message passing", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "crossbeam-utils", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.13.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "signal-hook", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "crossbeam-channel", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "fibonacci", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/fibonacci.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "matching", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/matching.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "stopwatch", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/examples/stopwatch.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "after", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/after.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "array", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/array.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "golang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/golang.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "iter", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/iter.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "list", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/list.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "mpsc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/mpsc.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "never", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/never.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "ready", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/ready.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "same_channel", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/same_channel.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "select", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "select_macro", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/select_macro.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "thread_locals", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/thread_locals.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "tick", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/tick.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zero", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/tests/zero.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "crossbeam", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/benches/crossbeam.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "crossbeam-utils": [ - "dep:crossbeam-utils" - ], - "default": [ - "std" - ], - "std": [ - "crossbeam-utils/std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-channel-0.5.8/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [ - "algorithms", - "concurrency", - "data-structures" - ], - "keywords": [ - "channel", - "mpmc", - "select", - "golang", - "message" - ], - "readme": "README.md", - "repository": "https://github.com/crossbeam-rs/crossbeam", - "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.38" - }, - { - "name": "crossbeam-utils", - "version": "0.8.15", - "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Utilities for concurrent programming", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "loom", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": "cfg(crossbeam_loom)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "crossbeam-utils", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "atomic_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/atomic_cell.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "cache_padded", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/cache_padded.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "parker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/parker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "sharded_lock", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/sharded_lock.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "thread", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/thread.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "wait_group", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/tests/wait_group.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "atomic_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/benches/atomic_cell.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std" - ], - "loom": [ - "dep:loom" - ], - "nightly": [], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/crossbeam-utils-0.8.15/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [ - "algorithms", - "concurrency", - "data-structures", - "no-std" - ], - "keywords": [ - "scoped", - "thread", - "atomic", - "cache" - ], - "readme": "README.md", - "repository": "https://github.com/crossbeam-rs/crossbeam", - "homepage": "https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-utils", - "documentation": null, - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.38" - }, - { - "name": "encoding_rs", - "version": "0.8.32", - "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(Apache-2.0 OR MIT) AND BSD-3-Clause", - "license_file": null, - "description": "A Gecko-oriented implementation of the Encoding Standard", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "packed_simd_2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.4", - "kind": null, - "rename": "packed_simd", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bincode", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "encoding_rs", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "alloc": [], - "default": [ - "alloc" - ], - "fast-big5-hanzi-encode": [], - "fast-gb-hanzi-encode": [], - "fast-hangul-encode": [], - "fast-hanja-encode": [], - "fast-kanji-encode": [], - "fast-legacy-encode": [ - "fast-hangul-encode", - "fast-hanja-encode", - "fast-kanji-encode", - "fast-gb-hanzi-encode", - "fast-big5-hanzi-encode" - ], - "less-slow-big5-hanzi-encode": [], - "less-slow-gb-hanzi-encode": [], - "less-slow-kanji-encode": [], - "packed_simd": [ - "dep:packed_simd" - ], - "serde": [ - "dep:serde" - ], - "simd-accel": [ - "packed_simd", - "packed_simd/into_bits" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs-0.8.32/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Henri Sivonen " - ], - "categories": [ - "text-processing", - "encoding", - "web-programming", - "internationalization" - ], - "keywords": [ - "encoding", - "web", - "unicode", - "charset" - ], - "readme": "README.md", - "repository": "https://github.com/hsivonen/encoding_rs", - "homepage": "https://docs.rs/encoding_rs/", - "documentation": "https://docs.rs/encoding_rs/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "encoding_rs_io", - "version": "0.1.7", - "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Streaming transcoding for encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "encoding_rs_io", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/encoding_rs_io-0.1.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing", - "encoding", - "web-programming", - "email" - ], - "keywords": [ - "encoding", - "transcoding", - "stream", - "io", - "read" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/encoding_rs_io", - "homepage": null, - "documentation": "https://docs.rs/encoding_rs_io", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "fnv", - "version": "1.0.7", - "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 / MIT", - "license_file": null, - "description": "Fowler–Noll–Vo hash function", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "fnv", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "std": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/fnv-1.0.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/servo/rust-fnv", - "homepage": null, - "documentation": "https://doc.servo.org/fnv/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "glob", - "version": "0.3.1", - "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Support for matching file paths against Unix shell style patterns.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "glob", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "glob-std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/tests/glob-std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/glob-0.3.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "filesystem" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/glob", - "homepage": "https://github.com/rust-lang/glob", - "documentation": "https://docs.rs/glob/0.3.1", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "globset", - "version": "0.4.10", - "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Cross platform single glob and glob set matching. Glob set matching is the\nprocess of matching one or more glob patterns against a single candidate path\nsimultaneously, and returning all of the globs that matched.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "fnv", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "perf", - "std" - ], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.104", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "glob", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.45", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "globset", - "src_path": "$ROOT$ripgrep/crates/globset/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$ripgrep/crates/globset/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "log" - ], - "log": [ - "dep:log" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "serde" - ], - "simd-accel": [] - }, - "manifest_path": "$ROOT$ripgrep/crates/globset/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "glob", - "multiple", - "set", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/globset", - "documentation": "https://docs.rs/globset", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep", - "version": "0.2.11", - "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast line oriented regex searching as a library.\n", - "source": null, - "dependencies": [ - { - "name": "grep-cli", - "source": null, - "req": "^0.1.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/cli" - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "grep-pcre2", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/pcre2" - }, - { - "name": "grep-printer", - "source": null, - "req": "^0.1.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/printer" - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - }, - { - "name": "grep-searcher", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/searcher" - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.2.7", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep", - "src_path": "$ROOT$ripgrep/crates/grep/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "simplegrep", - "src_path": "$ROOT$ripgrep/crates/grep/examples/simplegrep.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "avx-accel": [], - "grep-pcre2": [ - "dep:grep-pcre2" - ], - "pcre2": [ - "grep-pcre2" - ], - "simd-accel": [ - "grep-searcher/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/grep/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/grep", - "documentation": "https://docs.rs/grep", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-cli", - "version": "0.1.7", - "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Utilities for search oriented command line applications.\n", - "source": null, - "dependencies": [ - { - "name": "atty", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "globset", - "source": null, - "req": "^0.4.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/globset" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-cli", - "src_path": "$ROOT$ripgrep/crates/cli/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/cli/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "cli", - "utility", - "util" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/cli", - "documentation": "https://docs.rs/grep-cli", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-matcher", - "version": "0.1.6", - "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A trait for regular expressions, with a focus on line oriented search.\n", - "source": null, - "dependencies": [ - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-matcher", - "src_path": "$ROOT$ripgrep/crates/matcher/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "integration", - "src_path": "$ROOT$ripgrep/crates/matcher/tests/tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/matcher/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "pattern", - "trait" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/matcher", - "documentation": "https://docs.rs/grep-matcher", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-pcre2", - "version": "0.1.6", - "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Use PCRE2 with the 'grep' crate.\n", - "source": null, - "dependencies": [ - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "pcre2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-pcre2", - "src_path": "$ROOT$ripgrep/crates/pcre2/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/pcre2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "pcre", - "backreference", - "look" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/pcre2", - "documentation": "https://docs.rs/grep-pcre2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-printer", - "version": "0.1.7", - "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "An implementation of the grep crate's Sink trait that provides standard\nprinting of search results, similar to grep itself.\n", - "source": null, - "dependencies": [ - { - "name": "base64", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.20.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "grep-searcher", - "source": null, - "req": "^0.1.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/searcher" - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.27", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-printer", - "src_path": "$ROOT$ripgrep/crates/printer/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "base64": [ - "dep:base64" - ], - "default": [ - "serde1" - ], - "serde": [ - "dep:serde" - ], - "serde1": [ - "base64", - "serde", - "serde_json" - ], - "serde_json": [ - "dep:serde_json" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/printer/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "grep", - "pattern", - "print", - "printer", - "sink" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/printer", - "documentation": "https://docs.rs/grep-printer", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-regex", - "version": "0.1.11", - "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Use Rust's regex library with the 'grep' crate.\n", - "source": null, - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-regex", - "src_path": "$ROOT$ripgrep/crates/regex/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$ripgrep/crates/regex/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "search", - "pattern", - "line" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/regex", - "documentation": "https://docs.rs/grep-regex", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "grep-searcher", - "version": "0.1.11", - "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "Fast line oriented regex searching as a library.\n", - "source": null, - "dependencies": [ - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "bytecount", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "encoding_rs", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.14", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "encoding_rs_io", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-matcher", - "source": null, - "req": "^0.1.6", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/matcher" - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memmap2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.3", - "kind": null, - "rename": "memmap", - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "grep-regex", - "source": null, - "req": "^0.1.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/regex" - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "grep-searcher", - "src_path": "$ROOT$ripgrep/crates/searcher/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "search-stdin", - "src_path": "$ROOT$ripgrep/crates/searcher/examples/search-stdin.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "avx-accel": [], - "default": [ - "bytecount/runtime-dispatch-simd" - ], - "simd-accel": [ - "encoding_rs/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/searcher/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/searcher", - "documentation": "https://docs.rs/grep-searcher", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "hermit-abi", - "version": "0.1.19", - "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "hermit-abi is small interface to call functions from the unikernel RustyHermit.\nIt is used to build the target `x86_64-unknown-hermit`.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.51", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "hermit-abi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "docs": [], - "rustc-dep-of-std": [ - "core", - "compiler_builtins/rustc-dep-of-std", - "libc/rustc-dep-of-std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/hermit-abi-0.1.19/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-unknown-hermit", - "features": [ - "docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Stefan Lankes" - ], - "categories": [ - "os" - ], - "keywords": [ - "unikernel", - "libos" - ], - "readme": "README.md", - "repository": "https://github.com/hermitcore/libhermit-rs", - "homepage": null, - "documentation": "https://hermitcore.github.io/rusty-hermit/hermit_abi", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "ignore", - "version": "0.4.20", - "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A fast library for efficiently matching ignore files such as `.gitignore`\nagainst file paths.\n", - "source": null, - "dependencies": [ - { - "name": "globset", - "source": null, - "req": "^0.4.10", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/globset" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.2.7", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "crossbeam-channel", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "ignore", - "src_path": "$ROOT$ripgrep/crates/ignore/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "walk", - "src_path": "$ROOT$ripgrep/crates/ignore/examples/walk.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "gitignore_matched_path_or_any_parents_tests", - "src_path": "$ROOT$ripgrep/crates/ignore/tests/gitignore_matched_path_or_any_parents_tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "simd-accel": [ - "globset/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/crates/ignore/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "glob", - "ignore", - "gitignore", - "pattern", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", - "homepage": "https://github.com/BurntSushi/ripgrep/tree/master/crates/ignore", - "documentation": "https://docs.rs/ignore", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "itoa", - "version": "1.0.6", - "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Fast integer primitive to string conversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "no-panic", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "itoa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "no-panic": [ - "dep:no-panic" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/itoa-1.0.6/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "value-formatting", - "no-std" - ], - "keywords": [ - "integer" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/itoa", - "homepage": null, - "documentation": "https://docs.rs/itoa", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "jemalloc-sys", - "version": "0.5.3+5.3.0-patched", - "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Rust FFI bindings to jemalloc\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.13", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jemalloc-sys", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloc_conf_empty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_empty.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloc_conf_set", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/malloc_conf_set.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "unprefixed_malloc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/tests/unprefixed_malloc.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "background_threads": [ - "background_threads_runtime_support" - ], - "background_threads_runtime_support": [], - "debug": [], - "default": [ - "background_threads_runtime_support" - ], - "disable_initial_exec_tls": [], - "profiling": [], - "stats": [], - "unprefixed_malloc_on_supported_platforms": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemalloc-sys-0.5.3+5.3.0-patched/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustdoc-args": [ - "--cfg", - "jemallocator_docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Alex Crichton ", - "Gonzalo Brito Gadeschi ", - "The TiKV Project Developers" - ], - "categories": [], - "keywords": [ - "allocator", - "jemalloc" - ], - "readme": "README.md", - "repository": "https://github.com/tikv/jemallocator", - "homepage": "https://github.com/tikv/jemallocator", - "documentation": "https://docs.rs/jemallocator-sys", - "edition": "2018", - "links": "jemalloc", - "default_run": null, - "rust_version": null - }, - { - "name": "jemallocator", - "version": "0.5.0", - "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A Rust allocator backed by jemalloc\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "jemalloc-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.8", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "paste", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jemallocator", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "background_thread_defaults", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_defaults.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "background_thread_enabled", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/background_thread_enabled.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "ffi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/ffi.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "grow_in_place", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/grow_in_place.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "malloctl", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/malloctl.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "shrink_in_place", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/shrink_in_place.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "smoke", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "smoke_ffi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/smoke_ffi.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "usable_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/tests/usable_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "roundtrip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/benches/roundtrip.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc_trait": [], - "background_threads": [ - "jemalloc-sys/background_threads" - ], - "background_threads_runtime_support": [ - "jemalloc-sys/background_threads_runtime_support" - ], - "debug": [ - "jemalloc-sys/debug" - ], - "default": [ - "background_threads_runtime_support" - ], - "disable_initial_exec_tls": [ - "jemalloc-sys/disable_initial_exec_tls" - ], - "profiling": [ - "jemalloc-sys/profiling" - ], - "stats": [ - "jemalloc-sys/stats" - ], - "unprefixed_malloc_on_supported_platforms": [ - "jemalloc-sys/unprefixed_malloc_on_supported_platforms" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jemallocator-0.5.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [], - "rustdoc-args": [ - "--cfg", - "jemallocator_docs" - ] - } - } - }, - "publish": null, - "authors": [ - "Alex Crichton ", - "Gonzalo Brito Gadeschi ", - "Simon Sapin ", - "Steven Fackler ", - "The TiKV Project Developers" - ], - "categories": [ - "memory-management", - "api-bindings" - ], - "keywords": [ - "allocator", - "jemalloc" - ], - "readme": "README.md", - "repository": "https://github.com/tikv/jemallocator", - "homepage": "https://github.com/tikv/jemallocator", - "documentation": "https://docs.rs/jemallocator", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "jobserver", - "version": "0.1.26", - "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "An implementation of the GNU make jobserver for Rust\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "futures", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tokio-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tokio-process", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.50", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "jobserver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "client", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "server", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/server.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "client-of-myself", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/client-of-myself.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "make-as-a-client", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/make-as-a-client.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "helper", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/tests/helper.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/jobserver-0.1.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/alexcrichton/jobserver-rs", - "homepage": "https://github.com/alexcrichton/jobserver-rs", - "documentation": "https://docs.rs/jobserver", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "lazy_static", - "version": "1.4.0", - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "A macro for declaring lazily evaluated statics in Rust.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "spin", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "no_std", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/no_std.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "spin": [ - "dep:spin" - ], - "spin_no_std": [ - "spin" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/lazy_static-1.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Marvin Löbel " - ], - "categories": [ - "no-std", - "rust-patterns", - "memory-management" - ], - "keywords": [ - "macro", - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang-nursery/lazy-static.rs", - "homepage": null, - "documentation": "https://docs.rs/lazy_static", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "libc", - "version": "0.2.142", - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings to platform libraries like libc.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "libc", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "const_fn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/tests/const_fn.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "align": [], - "const-extern-fn": [], - "default": [ - "std" - ], - "extra_traits": [], - "rustc-dep-of-std": [ - "align", - "rustc-std-workspace-core" - ], - "rustc-std-workspace-core": [ - "dep:rustc-std-workspace-core" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/libc-0.2.142/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "const-extern-fn", - "extra_traits" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os" - ], - "keywords": [ - "libc", - "ffi", - "bindings", - "operating", - "system" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/libc", - "homepage": "https://github.com/rust-lang/libc", - "documentation": "https://docs.rs/libc/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "log", - "version": "0.4.17", - "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A lightweight logging facade for Rust\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "sval", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.5", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "value-bag", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.9", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_test", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "sval", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.5", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "value-bag", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.0-alpha.9", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "test" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "log", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "filters", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/filters.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "macros", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/tests/macros.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "value", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/benches/value.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "kv_unstable": [ - "value-bag" - ], - "kv_unstable_serde": [ - "kv_unstable_std", - "value-bag/serde", - "serde" - ], - "kv_unstable_std": [ - "std", - "kv_unstable", - "value-bag/error" - ], - "kv_unstable_sval": [ - "kv_unstable", - "value-bag/sval", - "sval" - ], - "max_level_debug": [], - "max_level_error": [], - "max_level_info": [], - "max_level_off": [], - "max_level_trace": [], - "max_level_warn": [], - "release_max_level_debug": [], - "release_max_level_error": [], - "release_max_level_info": [], - "release_max_level_off": [], - "release_max_level_trace": [], - "release_max_level_warn": [], - "serde": [ - "dep:serde" - ], - "std": [], - "sval": [ - "dep:sval" - ], - "value-bag": [ - "dep:value-bag" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/log-0.4.17/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "std", - "serde", - "kv_unstable_std", - "kv_unstable_sval", - "kv_unstable_serde" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "development-tools::debugging" - ], - "keywords": [ - "logging" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/log", - "homepage": null, - "documentation": "https://docs.rs/log", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memchr", - "version": "2.5.0", - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Safe interface to memchr.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.18", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memchr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [ - "std" - ], - "libc": [ - "dep:libc" - ], - "rustc-dep-of-std": [ - "core", - "compiler_builtins" - ], - "std": [], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memchr-2.5.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant ", - "bluss" - ], - "categories": [], - "keywords": [ - "memchr", - "char", - "scan", - "strchr", - "string" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/memchr", - "homepage": "https://github.com/BurntSushi/memchr", - "documentation": "https://docs.rs/memchr/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "memmap2", - "version": "0.5.10", - "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Cross-platform Rust API for memory-mapped file IO", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "stable_deref_trait", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "owning_ref", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tempfile", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(unix)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "memmap2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "cat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/examples/cat.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "stable_deref_trait": [ - "dep:stable_deref_trait" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/memmap2-0.5.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Dan Burkert ", - "Yevhenii Reizner " - ], - "categories": [], - "keywords": [ - "mmap", - "memory-map", - "io", - "file" - ], - "readme": "README.md", - "repository": "https://github.com/RazrFalcon/memmap2-rs", - "homepage": null, - "documentation": "https://docs.rs/memmap2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "once_cell", - "version": "1.17.1", - "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Single assignment cells and lazy values.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "atomic-polyfill", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": "atomic_polyfill", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "critical-section", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": "critical_section", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "parking_lot_core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.9.3", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "critical-section", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.1", - "kind": "dev", - "rename": "critical_section", - "optional": false, - "uses_default_features": true, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "crossbeam-utils", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.7", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "once_cell", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench_acquire", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_acquire.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "bench_vs_lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/bench_vs_lazy_static.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "lazy_static", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/lazy_static.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "reentrant_init_deadlocks", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/reentrant_init_deadlocks.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/regex.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "test_synchronization", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/examples/test_synchronization.rs", - "edition": "2021", - "required-features": [ - "std" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "it", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/tests/it.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "alloc": [ - "race" - ], - "atomic-polyfill": [ - "critical-section" - ], - "atomic_polyfill": [ - "dep:atomic_polyfill" - ], - "critical-section": [ - "critical_section", - "atomic_polyfill" - ], - "critical_section": [ - "dep:critical_section" - ], - "default": [ - "std" - ], - "parking_lot": [ - "parking_lot_core" - ], - "parking_lot_core": [ - "dep:parking_lot_core" - ], - "race": [], - "std": [ - "alloc" - ], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/once_cell-1.17.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true - } - } - }, - "publish": null, - "authors": [ - "Aleksey Kladov " - ], - "categories": [ - "rust-patterns", - "memory-management" - ], - "keywords": [ - "lazy", - "static" - ], - "readme": "README.md", - "repository": "https://github.com/matklad/once_cell", - "homepage": null, - "documentation": "https://docs.rs/once_cell", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "pcre2", - "version": "0.2.3", - "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "High level wrapper library for PCRE2.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.46", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "pcre2-sys", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "thread_local", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pcre2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-0.2.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "pcre", - "pcre2", - "regex", - "jit", - "perl" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/rust-pcre2", - "homepage": "https://github.com/BurntSushi/rust-pcre2", - "documentation": "https://docs.rs/pcre2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pcre2-sys", - "version": "0.2.5", - "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Low level bindings to PCRE2.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "libc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "cc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "parallel" - ], - "target": null, - "registry": null - }, - { - "name": "pkg-config", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.13", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pcre2-sys", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pcre2-sys-0.2.5/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "external-ffi-bindings" - ], - "keywords": [ - "pcre", - "pcre2", - "regex", - "jit" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/rust-pcre2", - "homepage": "https://github.com/BurntSushi/rust-pcre2", - "documentation": "https://docs.rs/pcre2-sys", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "pkg-config", - "version": "0.3.26", - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A library to run the pkg-config system tool at build time in order to be used in\nCargo build scripts.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "pkg-config", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/tests/test.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/pkg-config-0.3.26/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Alex Crichton " - ], - "categories": [], - "keywords": [ - "build-dependencies" - ], - "readme": "README.md", - "repository": "https://github.com/rust-lang/pkg-config-rs", - "homepage": null, - "documentation": "https://docs.rs/pkg-config", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "proc-macro2", - "version": "1.0.56", - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "proc-macro2", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "comments", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/comments.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "features", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/features.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "marker", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/marker.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_fmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_fmt.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/tests/test_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "nightly": [], - "proc-macro": [], - "span-locations": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/proc-macro2-1.0.56/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "rustc-args": [ - "--cfg", - "procmacro2_semver_exempt" - ], - "rustdoc-args": [ - "--cfg", - "procmacro2_semver_exempt", - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "span-locations" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay ", - "Alex Crichton " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/proc-macro2", - "homepage": null, - "documentation": "https://docs.rs/proc-macro2", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "quote", - "version": "1.0.26", - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Quasi-quoting macro quote!(...)", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.52", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.66", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "quote", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "proc-macro" - ], - "proc-macro": [ - "proc-macro2/proc-macro" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/quote-1.0.26/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/quote", - "homepage": null, - "documentation": "https://docs.rs/quote/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "regex", - "version": "1.8.1", - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "An implementation of regular expressions for Rust. This implementation uses\nfinite automata and guarantees linear time matching on all inputs.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "aho-corasick", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "memchr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.5.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quickcheck", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "getrandom", - "small_rng" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-replace", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-replace.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single-cheat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single-cheat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna-single", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna-single.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "shootout-regex-dna", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/examples/shootout-regex-dna.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_default_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "nfa-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_nfa_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-utf8bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_utf8bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "backtrack-bytes", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_backtrack_bytes.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "crates-regex", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/tests/test_crates_regex.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "aho-corasick": [ - "dep:aho-corasick" - ], - "default": [ - "std", - "perf", - "unicode", - "regex-syntax/default" - ], - "memchr": [ - "dep:memchr" - ], - "pattern": [], - "perf": [ - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal" - ], - "perf-cache": [], - "perf-dfa": [], - "perf-inline": [], - "perf-literal": [ - "aho-corasick", - "memchr" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment", - "regex-syntax/unicode" - ], - "unicode-age": [ - "regex-syntax/unicode-age" - ], - "unicode-bool": [ - "regex-syntax/unicode-bool" - ], - "unicode-case": [ - "regex-syntax/unicode-case" - ], - "unicode-gencat": [ - "regex-syntax/unicode-gencat" - ], - "unicode-perl": [ - "regex-syntax/unicode-perl" - ], - "unicode-script": [ - "regex-syntax/unicode-script" - ], - "unicode-segment": [ - "regex-syntax/unicode-segment" - ], - "unstable": [ - "pattern" - ], - "use_std": [ - "std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-1.8.1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [ - "text-processing" - ], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "regex-automata", - "version": "0.1.10", - "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Automata construction and matching using regular expressions.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex-syntax", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6.16", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.82", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_bytes", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.82", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "toml", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-automata", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "default", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/tests/tests.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - } - ], - "features": { - "default": [ - "std" - ], - "fst": [ - "dep:fst" - ], - "regex-syntax": [ - "dep:regex-syntax" - ], - "std": [ - "regex-syntax" - ], - "transducer": [ - "std", - "fst" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-automata-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "text-processing" - ], - "keywords": [ - "regex", - "dfa", - "automata", - "automaton", - "nfa" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/regex-automata", - "homepage": "https://github.com/BurntSushi/regex-automata", - "documentation": "https://docs.rs/regex-automata", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.6.29", - "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "unicode" - ], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.6.29/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "regex-syntax", - "version": "0.7.1", - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A regular expression parser.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "regex-syntax", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/benches/bench.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [ - "std", - "unicode" - ], - "std": [], - "unicode": [ - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ], - "unicode-age": [], - "unicode-bool": [], - "unicode-case": [], - "unicode-gencat": [], - "unicode-perl": [], - "unicode-script": [], - "unicode-segment": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/regex-syntax-0.7.1/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "docsrs" - ] - } - } - }, - "publish": null, - "authors": [ - "The Rust Project Developers" - ], - "categories": [], - "keywords": [], - "readme": "README.md", - "repository": "https://github.com/rust-lang/regex", - "homepage": "https://github.com/rust-lang/regex", - "documentation": "https://docs.rs/regex-syntax", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.60.0" - }, - { - "name": "ripgrep", - "version": "13.0.0", - "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "ripgrep is a line-oriented search tool that recursively searches the current\ndirectory for a regex pattern while respecting gitignore rules. ripgrep has\nfirst class support on Windows, macOS and Linux.\n", - "source": null, - "dependencies": [ - { - "name": "bstr", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.33.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "suggestions" - ], - "target": null, - "registry": null - }, - { - "name": "grep", - "source": null, - "req": "^0.2.11", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/grep" - }, - { - "name": "ignore", - "source": null, - "req": "^0.4.19", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$ripgrep/crates/ignore" - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "log", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.3.5", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_json", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.23", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.77", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "clap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.33.0", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [ - "suggestions" - ], - "target": null, - "registry": null - }, - { - "name": "lazy_static", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.1.0", - "kind": "build", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "jemallocator", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.5.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "bin" - ], - "crate_types": [ - "bin" - ], - "name": "rg", - "src_path": "$ROOT$ripgrep/crates/core/main.rs", - "edition": "2018", - "doc": true, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "integration", - "src_path": "$ROOT$ripgrep/tests/tests.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$ripgrep/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "pcre2": [ - "grep/pcre2" - ], - "simd-accel": [ - "grep/simd-accel" - ] - }, - "manifest_path": "$ROOT$ripgrep/Cargo.toml", - "metadata": { - "deb": { - "assets": [ - [ - "target/release/rg", - "usr/bin/", - "755" - ], - [ - "COPYING", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "LICENSE-MIT", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "UNLICENSE", - "usr/share/doc/ripgrep/", - "644" - ], - [ - "CHANGELOG.md", - "usr/share/doc/ripgrep/CHANGELOG", - "644" - ], - [ - "README.md", - "usr/share/doc/ripgrep/README", - "644" - ], - [ - "FAQ.md", - "usr/share/doc/ripgrep/FAQ", - "644" - ], - [ - "deployment/deb/rg.1", - "usr/share/man/man1/rg.1", - "644" - ], - [ - "deployment/deb/rg.bash", - "usr/share/bash-completion/completions/rg", - "644" - ], - [ - "deployment/deb/rg.fish", - "usr/share/fish/vendor_completions.d/rg.fish", - "644" - ], - [ - "deployment/deb/_rg", - "usr/share/zsh/vendor-completions/", - "644" - ] - ], - "extended-description": "ripgrep (rg) recursively searches your current directory for a regex pattern.\nBy default, ripgrep will respect your .gitignore and automatically skip hidden\nfiles/directories and binary files.\n", - "features": [ - "pcre2" - ], - "section": "utils" - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "command-line-utilities", - "text-processing" - ], - "keywords": [ - "regex", - "grep", - "egrep", - "search", - "pattern" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/ripgrep", - "homepage": "https://github.com/BurntSushi/ripgrep", - "documentation": "https://github.com/BurntSushi/ripgrep", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.65" - }, - { - "name": "ryu", - "version": "1.0.13", - "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Apache-2.0 OR BSL-1.0", - "license_file": null, - "description": "Fast floating point to string conversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "no-panic", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "num_cpus", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_xorshift", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "ryu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "upstream_benchmark", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/examples/upstream_benchmark.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "common_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/common_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "d2s_table_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_table_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "d2s_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/d2s_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "exhaustive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/exhaustive.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "f2s_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/f2s_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "s2d_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2d_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "s2f_test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/tests/s2f_test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "bench", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/benches/bench.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "no-panic": [ - "dep:no-panic" - ], - "small": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/ryu-1.0.13/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "value-formatting", - "no-std" - ], - "keywords": [ - "float" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/ryu", - "homepage": null, - "documentation": "https://docs.rs/ryu", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "same-file", - "version": "1.0.6", - "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "A simple crate for determining whether two file paths point to the same file.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "same-file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "is_same_file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_same_file.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "is_stderr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/examples/is_stderr.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/same-file-1.0.6/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "same", - "file", - "equal", - "inode" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/same-file", - "homepage": "https://github.com/BurntSushi/same-file", - "documentation": "https://docs.rs/same-file", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "serde", - "version": "1.0.160", - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A generic serialization/deserialization framework", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "=1.0.160", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [], - "default": [ - "std" - ], - "derive": [ - "serde_derive" - ], - "rc": [], - "serde_derive": [ - "dep:serde_derive" - ], - "std": [], - "unstable": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "derive" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "derive", - "rc" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://docs.rs/serde", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.19" - }, - { - "name": "serde_derive", - "version": "1.0.160", - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "proc-macro" - ], - "crate_types": [ - "proc-macro" - ], - "name": "serde_derive", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "default": [], - "deserialize_in_place": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_derive-1.0.160/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "no-std" - ], - "keywords": [ - "serde", - "serialization", - "no_std", - "derive" - ], - "readme": "crates-io.md", - "repository": "https://github.com/serde-rs/serde", - "homepage": "https://serde.rs", - "documentation": "https://serde.rs/derive.html", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "serde_json", - "version": "1.0.96", - "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "A JSON serialization file format", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "indexmap", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.5.2", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "std" - ], - "target": null, - "registry": null - }, - { - "name": "itoa", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ryu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.100", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "indoc", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.100", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "derive" - ], - "target": null, - "registry": null - }, - { - "name": "serde_bytes", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_derive", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "serde_stacker", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "trybuild", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.49", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "diff" - ], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "serde_json", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compiletest", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/compiletest.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "debug", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/debug.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lexical", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/lexical.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "map", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/map.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/regression.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/stream.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/tests/test.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/build.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "alloc": [ - "serde/alloc" - ], - "arbitrary_precision": [], - "default": [ - "std" - ], - "float_roundtrip": [], - "indexmap": [ - "dep:indexmap" - ], - "preserve_order": [ - "indexmap", - "std" - ], - "raw_value": [], - "std": [ - "serde/std" - ], - "unbounded_depth": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/serde_json-1.0.96/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "features": [ - "raw_value", - "unbounded_depth" - ], - "rustdoc-args": [ - "--cfg", - "docsrs" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "raw_value" - ] - } - }, - "publish": null, - "authors": [ - "Erick Tryzelaar ", - "David Tolnay " - ], - "categories": [ - "encoding", - "parser-implementations", - "no-std" - ], - "keywords": [ - "json", - "serde", - "serialization" - ], - "readme": "README.md", - "repository": "https://github.com/serde-rs/json", - "homepage": null, - "documentation": "https://docs.rs/serde_json", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.36" - }, - { - "name": "strsim", - "version": "0.8.0", - "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Implementations of string similarity metrics.\nIncludes Hamming, Levenshtein, OSA, Damerau-Levenshtein, Jaro, and Jaro-Winkler.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "strsim", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "lib", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/tests/lib.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "benches", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/benches/benches.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/strsim-0.8.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Danny Guo " - ], - "categories": [], - "keywords": [ - "string", - "similarity", - "Hamming", - "Levenshtein", - "Jaro" - ], - "readme": "README.md", - "repository": "https://github.com/dguo/strsim-rs", - "homepage": "https://github.com/dguo/strsim-rs", - "documentation": "https://docs.rs/strsim/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "syn", - "version": "2.0.15", - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Parser for Rust source code", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "proc-macro2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.55", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "quote", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.25", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-ident", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "anyhow", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "automod", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "flate2", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "insta", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rayon", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ref-cast", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "regex", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "reqwest", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.11", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "blocking" - ], - "target": null, - "registry": null - }, - { - "name": "rustversion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "syn-test-suite", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "tar", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.16", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "termcolor", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "walkdir", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^2.3.2", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "syn", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "regression", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/regression.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_asyncness", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_asyncness.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_attribute", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_attribute.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_derive_input", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_derive_input.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_expr", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_expr.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_generics", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_generics.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_grouping", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_grouping.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ident.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_item", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_item.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_iterators", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_iterators.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_lit", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_lit.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_meta", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_meta.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_buffer", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_buffer.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_parse_stream", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_parse_stream.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_pat", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_pat.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_path", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_path.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_precedence", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_precedence.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_receiver", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_receiver.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_round_trip", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_round_trip.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_shebang", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_shebang.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_should_parse", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_should_parse.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_size.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_stmt", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_stmt.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_token_trees", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_token_trees.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_ty", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_ty.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "test_visibility", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/test_visibility.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "zzz_stable", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/tests/zzz_stable.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "rust", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/rust.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "file", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/benches/file.rs", - "edition": "2021", - "required-features": [ - "full", - "parsing" - ], - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "clone-impls": [], - "default": [ - "derive", - "parsing", - "printing", - "clone-impls", - "proc-macro" - ], - "derive": [], - "extra-traits": [], - "fold": [], - "full": [], - "parsing": [], - "printing": [ - "quote" - ], - "proc-macro": [ - "proc-macro2/proc-macro", - "quote/proc-macro" - ], - "quote": [ - "dep:quote" - ], - "test": [ - "syn-test-suite/all-features" - ], - "visit": [], - "visit-mut": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/syn-2.0.15/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true, - "rustdoc-args": [ - "--cfg", - "doc_cfg" - ], - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - }, - "playground": { - "features": [ - "full", - "visit", - "visit-mut", - "fold", - "extra-traits" - ] - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "parser-implementations" - ], - "keywords": [ - "macros", - "syn" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/syn", - "homepage": null, - "documentation": "https://docs.rs/syn", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": "1.56" - }, - { - "name": "termcolor", - "version": "1.2.0", - "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense OR MIT", - "license_file": null, - "description": "A simple cross platform library for writing colored text to a terminal.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "termcolor", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/termcolor-1.2.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [], - "keywords": [ - "windows", - "win", - "color", - "ansi", - "console" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/termcolor", - "homepage": "https://github.com/BurntSushi/termcolor", - "documentation": "https://docs.rs/termcolor", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "textwrap", - "version": "0.11.0", - "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT", - "license_file": null, - "description": "Textwrap is a small library for word wrapping, indenting, and\ndedenting strings.\n\nYou can use it to format strings (such as help and error messages) for\ndisplay in commandline applications. It is designed to be efficient\nand handle Unicode characters correctly.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "hyphenation", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.7.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [ - "embed_all" - ], - "target": null, - "registry": null - }, - { - "name": "term_size", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3.0", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-width", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "lipsum", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand_xorshift", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "version-sync", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.6", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "textwrap", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "layout", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/layout.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "example" - ], - "crate_types": [ - "bin" - ], - "name": "termwidth", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/examples/termwidth.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "version-numbers", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/tests/version-numbers.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "linear", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/benches/linear.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "hyphenation": [ - "dep:hyphenation" - ], - "term_size": [ - "dep:term_size" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/textwrap-0.11.0/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "all-features": true - } - } - }, - "publish": null, - "authors": [ - "Martin Geisler " - ], - "categories": [ - "text-processing", - "command-line-interface" - ], - "keywords": [ - "text", - "formatting", - "wrap", - "typesetting", - "hyphenation" - ], - "readme": "README.md", - "repository": "https://github.com/mgeisler/textwrap", - "homepage": null, - "documentation": "https://docs.rs/textwrap/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "thread_local", - "version": "1.1.7", - "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT OR Apache-2.0", - "license_file": null, - "description": "Per-object thread-local storage", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "cfg-if", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.0", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "once_cell", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.5.2", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4.0", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "thread_local", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "thread_local", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/benches/thread_local.rs", - "edition": "2021", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "nightly": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/thread_local-1.1.7/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Amanieu d'Antras " - ], - "categories": [], - "keywords": [ - "thread_local", - "concurrent", - "thread" - ], - "readme": "README.md", - "repository": "https://github.com/Amanieu/thread_local-rs", - "homepage": null, - "documentation": "https://docs.rs/thread_local/", - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "unicode-ident", - "version": "1.0.8", - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "(MIT OR Apache-2.0) AND Unicode-DFS-2016", - "license_file": null, - "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "criterion", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "fst", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rand", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.8", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "small_rng" - ], - "target": null, - "registry": null - }, - { - "name": "roaring", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.10", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "ucd-trie", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": false, - "features": [], - "target": null, - "registry": null - }, - { - "name": "unicode-xid", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.2.4", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-ident", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "compare", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/compare.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "test" - ], - "crate_types": [ - "bin" - ], - "name": "static_size", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/tests/static_size.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": true - }, - { - "kind": [ - "bench" - ], - "crate_types": [ - "bin" - ], - "name": "xid", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/benches/xid.rs", - "edition": "2018", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-ident-1.0.8/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-unknown-linux-gnu" - ] - } - } - }, - "publish": null, - "authors": [ - "David Tolnay " - ], - "categories": [ - "development-tools::procedural-macro-helpers", - "no-std" - ], - "keywords": [ - "unicode", - "xid" - ], - "readme": "README.md", - "repository": "https://github.com/dtolnay/unicode-ident", - "homepage": null, - "documentation": "https://docs.rs/unicode-ident", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": "1.31" - }, - { - "name": "unicode-width", - "version": "0.1.10", - "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Determine displayed width of `char` and `str` types\naccording to Unicode Standard Annex #11 rules.\n", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "compiler_builtins", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1", - "kind": null, - "rename": null, - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-core", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "core", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "rustc-std-workspace-std", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0", - "kind": null, - "rename": "std", - "optional": true, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "unicode-width", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": { - "bench": [], - "compiler_builtins": [ - "dep:compiler_builtins" - ], - "core": [ - "dep:core" - ], - "default": [], - "no_std": [], - "rustc-dep-of-std": [ - "std", - "core", - "compiler_builtins" - ], - "std": [ - "dep:std" - ] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/unicode-width-0.1.10/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "kwantam ", - "Manish Goregaokar " - ], - "categories": [], - "keywords": [ - "text", - "width", - "unicode" - ], - "readme": "README.md", - "repository": "https://github.com/unicode-rs/unicode-width", - "homepage": "https://github.com/unicode-rs/unicode-width", - "documentation": "https://unicode-rs.github.io/unicode-width", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "walkdir", - "version": "2.3.3", - "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "Recursively walk a directory.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "same-file", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^1.0.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "doc-comment", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": "dev", - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null - }, - { - "name": "winapi-util", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.1.1", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "walkdir", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/walkdir-2.3.3/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "filesystem" - ], - "keywords": [ - "directory", - "recursive", - "walk", - "iterator" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/walkdir", - "homepage": "https://github.com/BurntSushi/walkdir", - "documentation": "https://docs.rs/walkdir/", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi", - "version": "0.3.9", - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Raw FFI bindings for all of Windows API.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi-i686-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "i686-pc-windows-gnu", - "registry": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.4", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": "x86_64-pc-windows-gnu", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": { - "accctrl": [], - "aclapi": [], - "activation": [], - "adhoc": [], - "appmgmt": [], - "audioclient": [], - "audiosessiontypes": [], - "avrt": [], - "basetsd": [], - "bcrypt": [], - "bits": [], - "bits10_1": [], - "bits1_5": [], - "bits2_0": [], - "bits2_5": [], - "bits3_0": [], - "bits4_0": [], - "bits5_0": [], - "bitscfg": [], - "bitsmsg": [], - "bluetoothapis": [], - "bluetoothleapis": [], - "bthdef": [], - "bthioctl": [], - "bthledef": [], - "bthsdpdef": [], - "bugcodes": [], - "cderr": [], - "cfg": [], - "cfgmgr32": [], - "cguid": [], - "combaseapi": [], - "coml2api": [], - "commapi": [], - "commctrl": [], - "commdlg": [], - "commoncontrols": [], - "consoleapi": [], - "corecrt": [], - "corsym": [], - "d2d1": [], - "d2d1_1": [], - "d2d1_2": [], - "d2d1_3": [], - "d2d1effectauthor": [], - "d2d1effects": [], - "d2d1effects_1": [], - "d2d1effects_2": [], - "d2d1svg": [], - "d2dbasetypes": [], - "d3d": [], - "d3d10": [], - "d3d10_1": [], - "d3d10_1shader": [], - "d3d10effect": [], - "d3d10misc": [], - "d3d10sdklayers": [], - "d3d10shader": [], - "d3d11": [], - "d3d11_1": [], - "d3d11_2": [], - "d3d11_3": [], - "d3d11_4": [], - "d3d11on12": [], - "d3d11sdklayers": [], - "d3d11shader": [], - "d3d11tokenizedprogramformat": [], - "d3d12": [], - "d3d12sdklayers": [], - "d3d12shader": [], - "d3d9": [], - "d3d9caps": [], - "d3d9types": [], - "d3dcommon": [], - "d3dcompiler": [], - "d3dcsx": [], - "d3dkmdt": [], - "d3dkmthk": [], - "d3dukmdt": [], - "d3dx10core": [], - "d3dx10math": [], - "d3dx10mesh": [], - "datetimeapi": [], - "davclnt": [], - "dbghelp": [], - "dbt": [], - "dcommon": [], - "dcomp": [], - "dcompanimation": [], - "dcomptypes": [], - "dde": [], - "ddraw": [], - "ddrawi": [], - "ddrawint": [], - "debug": [ - "impl-debug" - ], - "debugapi": [], - "devguid": [], - "devicetopology": [], - "devpkey": [], - "devpropdef": [], - "dinput": [], - "dinputd": [], - "dispex": [], - "dmksctl": [], - "dmusicc": [], - "docobj": [], - "documenttarget": [], - "dot1x": [], - "dpa_dsa": [], - "dpapi": [], - "dsgetdc": [], - "dsound": [], - "dsrole": [], - "dvp": [], - "dwmapi": [], - "dwrite": [], - "dwrite_1": [], - "dwrite_2": [], - "dwrite_3": [], - "dxdiag": [], - "dxfile": [], - "dxgi": [], - "dxgi1_2": [], - "dxgi1_3": [], - "dxgi1_4": [], - "dxgi1_5": [], - "dxgi1_6": [], - "dxgidebug": [], - "dxgiformat": [], - "dxgitype": [], - "dxva2api": [], - "dxvahd": [], - "eaptypes": [], - "enclaveapi": [], - "endpointvolume": [], - "errhandlingapi": [], - "everything": [], - "evntcons": [], - "evntprov": [], - "evntrace": [], - "excpt": [], - "exdisp": [], - "fibersapi": [], - "fileapi": [], - "functiondiscoverykeys_devpkey": [], - "gl-gl": [], - "guiddef": [], - "handleapi": [], - "heapapi": [], - "hidclass": [], - "hidpi": [], - "hidsdi": [], - "hidusage": [], - "highlevelmonitorconfigurationapi": [], - "hstring": [], - "http": [], - "ifdef": [], - "ifmib": [], - "imm": [], - "impl-debug": [], - "impl-default": [], - "in6addr": [], - "inaddr": [], - "inspectable": [], - "interlockedapi": [], - "intsafe": [], - "ioapiset": [], - "ipexport": [], - "iphlpapi": [], - "ipifcons": [], - "ipmib": [], - "iprtrmib": [], - "iptypes": [], - "jobapi": [], - "jobapi2": [], - "knownfolders": [], - "ks": [], - "ksmedia": [], - "ktmtypes": [], - "ktmw32": [], - "l2cmn": [], - "libloaderapi": [], - "limits": [], - "lmaccess": [], - "lmalert": [], - "lmapibuf": [], - "lmat": [], - "lmcons": [], - "lmdfs": [], - "lmerrlog": [], - "lmjoin": [], - "lmmsg": [], - "lmremutl": [], - "lmrepl": [], - "lmserver": [], - "lmshare": [], - "lmstats": [], - "lmsvc": [], - "lmuse": [], - "lmwksta": [], - "lowlevelmonitorconfigurationapi": [], - "lsalookup": [], - "memoryapi": [], - "minschannel": [], - "minwinbase": [], - "minwindef": [], - "mmdeviceapi": [], - "mmeapi": [], - "mmreg": [], - "mmsystem": [], - "mprapidef": [], - "msaatext": [], - "mscat": [], - "mschapp": [], - "mssip": [], - "mstcpip": [], - "mswsock": [], - "mswsockdef": [], - "namedpipeapi": [], - "namespaceapi": [], - "nb30": [], - "ncrypt": [], - "netioapi": [], - "nldef": [], - "ntddndis": [], - "ntddscsi": [], - "ntddser": [], - "ntdef": [], - "ntlsa": [], - "ntsecapi": [], - "ntstatus": [], - "oaidl": [], - "objbase": [], - "objidl": [], - "objidlbase": [], - "ocidl": [], - "ole2": [], - "oleauto": [], - "olectl": [], - "oleidl": [], - "opmapi": [], - "pdh": [], - "perflib": [], - "physicalmonitorenumerationapi": [], - "playsoundapi": [], - "portabledevice": [], - "portabledeviceapi": [], - "portabledevicetypes": [], - "powerbase": [], - "powersetting": [], - "powrprof": [], - "processenv": [], - "processsnapshot": [], - "processthreadsapi": [], - "processtopologyapi": [], - "profileapi": [], - "propidl": [], - "propkey": [], - "propkeydef": [], - "propsys": [], - "prsht": [], - "psapi": [], - "qos": [], - "realtimeapiset": [], - "reason": [], - "restartmanager": [], - "restrictederrorinfo": [], - "rmxfguid": [], - "roapi": [], - "robuffer": [], - "roerrorapi": [], - "rpc": [], - "rpcdce": [], - "rpcndr": [], - "rtinfo": [], - "sapi": [], - "sapi51": [], - "sapi53": [], - "sapiddk": [], - "sapiddk51": [], - "schannel": [], - "sddl": [], - "securityappcontainer": [], - "securitybaseapi": [], - "servprov": [], - "setupapi": [], - "shellapi": [], - "shellscalingapi": [], - "shlobj": [], - "shobjidl": [], - "shobjidl_core": [], - "shtypes": [], - "softpub": [], - "spapidef": [], - "spellcheck": [], - "sporder": [], - "sql": [], - "sqlext": [], - "sqltypes": [], - "sqlucode": [], - "sspi": [], - "std": [], - "stralign": [], - "stringapiset": [], - "strmif": [], - "subauth": [], - "synchapi": [], - "sysinfoapi": [], - "systemtopologyapi": [], - "taskschd": [], - "tcpestats": [], - "tcpmib": [], - "textstor": [], - "threadpoolapiset": [], - "threadpoollegacyapiset": [], - "timeapi": [], - "timezoneapi": [], - "tlhelp32": [], - "transportsettingcommon": [], - "tvout": [], - "udpmib": [], - "unknwnbase": [], - "urlhist": [], - "urlmon": [], - "usb": [], - "usbioctl": [], - "usbiodef": [], - "usbscan": [], - "usbspec": [], - "userenv": [], - "usp10": [], - "utilapiset": [], - "uxtheme": [], - "vadefs": [], - "vcruntime": [], - "vsbackup": [], - "vss": [], - "vsserror": [], - "vswriter": [], - "wbemads": [], - "wbemcli": [], - "wbemdisp": [], - "wbemprov": [], - "wbemtran": [], - "wct": [], - "werapi": [], - "winbase": [], - "wincodec": [], - "wincodecsdk": [], - "wincon": [], - "wincontypes": [], - "wincred": [], - "wincrypt": [], - "windef": [], - "windot11": [], - "windowsceip": [], - "windowsx": [], - "winefs": [], - "winerror": [], - "winevt": [], - "wingdi": [], - "winhttp": [], - "wininet": [], - "winineti": [], - "winioctl": [], - "winnetwk": [], - "winnls": [], - "winnt": [], - "winreg": [], - "winsafer": [], - "winscard": [], - "winsmcrd": [], - "winsock2": [], - "winspool": [], - "winstring": [], - "winsvc": [], - "wintrust": [], - "winusb": [], - "winusbio": [], - "winuser": [], - "winver": [], - "wlanapi": [], - "wlanihv": [], - "wlanihvtypes": [], - "wlantypes": [], - "wlclient": [], - "wmistr": [], - "wnnc": [], - "wow64apiset": [], - "wpdmtpextensions": [], - "ws2bth": [], - "ws2def": [], - "ws2ipdef": [], - "ws2spi": [], - "ws2tcpip": [], - "wtsapi32": [], - "wtypes": [], - "wtypesbase": [], - "xinput": [] - }, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-0.3.9/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "default-target": "x86_64-pc-windows-msvc", - "features": [ - "everything", - "impl-debug", - "impl-default" - ], - "targets": [ - "aarch64-pc-windows-msvc", - "i686-pc-windows-msvc", - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [ - "external-ffi-bindings", - "no-std", - "os::windows-apis" - ], - "keywords": [ - "windows", - "ffi", - "win32", - "com", - "directx" - ], - "readme": "README.md", - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": "https://docs.rs/winapi/", - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-i686-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the i686-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-i686-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-i686-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-util", - "version": "0.1.5", - "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "Unlicense/MIT", - "license_file": null, - "description": "A dumping ground for high level safe wrappers over winapi.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [ - { - "name": "winapi", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "req": "^0.3", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [ - "std", - "consoleapi", - "errhandlingapi", - "fileapi", - "minwindef", - "processenv", - "winbase", - "wincon", - "winerror", - "winnt" - ], - "target": "cfg(windows)", - "registry": null - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-util", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/src/lib.rs", - "edition": "2018", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-util-0.1.5/Cargo.toml", - "metadata": { - "docs": { - "rs": { - "targets": [ - "x86_64-pc-windows-msvc" - ] - } - } - }, - "publish": null, - "authors": [ - "Andrew Gallant " - ], - "categories": [ - "os::windows-apis", - "external-ffi-bindings" - ], - "keywords": [ - "windows", - "winapi", - "util", - "win" - ], - "readme": "README.md", - "repository": "https://github.com/BurntSushi/winapi-util", - "homepage": "https://github.com/BurntSushi/winapi-util", - "documentation": "https://docs.rs/winapi-util", - "edition": "2018", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "winapi-x86_64-pc-windows-gnu", - "version": "0.4.0", - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "license": "MIT/Apache-2.0", - "license_file": null, - "description": "Import libraries for the x86_64-pc-windows-gnu target. Please don't use this crate directly, depend on winapi instead.", - "source": "registry+https://github.com/rust-lang/crates.io-index", - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "winapi-x86_64-pc-windows-gnu", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/src/lib.rs", - "edition": "2015", - "doc": true, - "doctest": true, - "test": true - }, - { - "kind": [ - "custom-build" - ], - "crate_types": [ - "bin" - ], - "name": "build-script-build", - "src_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/build.rs", - "edition": "2015", - "doc": false, - "doctest": false, - "test": false - } - ], - "features": {}, - "manifest_path": "$ROOT$.cargo/registry/src/index.crates.io-6f17d22bba15001f/winapi-x86_64-pc-windows-gnu-0.4.0/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [ - "Peter Atashian " - ], - "categories": [], - "keywords": [ - "windows" - ], - "readme": null, - "repository": "https://github.com/retep998/winapi-rs", - "homepage": null, - "documentation": null, - "edition": "2015", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" - ], - "resolve": { - "nodes": [ - { - "id": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "perf-literal", - "std" - ] - }, - { - "id": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "hermit_abi", - "pkg": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(target_os = \"hermit\")" - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - }, - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default" - ] - }, - { - "id": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "once_cell", - "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_automata", - "pkg": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default", - "std", - "unicode" - ] - }, - { - "id": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "runtime-dispatch-simd" - ] - }, - { - "id": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "jobserver", - "pkg": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "jobserver", - "parallel" - ] - }, - { - "id": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bitflags", - "pkg": "bitflags 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "strsim", - "pkg": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "textwrap", - "pkg": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_width", - "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "strsim", - "suggestions" - ] - }, - { - "id": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "crossbeam_utils", - "pkg": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "crossbeam-utils", - "default", - "std" - ] - }, - { - "id": "crossbeam-utils 0.8.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "std" - ] - }, - { - "id": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default" - ] - }, - { - "id": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "encoding_rs", - "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "fnv", - "pkg": "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "glob", - "pkg": "glob 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [ - "default", - "log" - ] - }, - { - "id": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "dependencies": [ - "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "grep_cli", - "pkg": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_printer", - "pkg": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_searcher", - "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-cli 0.1.7 (path+file:///$ROOT$ripgrep/crates/cli)", - "dependencies": [ - "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "atty", - "pkg": "atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "globset", - "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dependencies": [ - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-pcre2 0.1.6 (path+file:///$ROOT$ripgrep/crates/pcre2)", - "dependencies": [ - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pcre2", - "pkg": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-printer 0.1.7 (path+file:///$ROOT$ripgrep/crates/printer)", - "dependencies": [ - "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "base64", - "pkg": "base64 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "grep_searcher", - "pkg": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "base64", - "default", - "serde", - "serde1", - "serde_json" - ] - }, - { - "id": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dependencies": [ - "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "grep-searcher 0.1.11 (path+file:///$ROOT$ripgrep/crates/searcher)", - "dependencies": [ - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "bytecount", - "pkg": "bytecount 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "encoding_rs", - "pkg": "encoding_rs 0.8.32 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "encoding_rs_io", - "pkg": "encoding_rs_io 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_matcher", - "pkg": "grep-matcher 0.1.6 (path+file:///$ROOT$ripgrep/crates/matcher)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "grep_regex", - "pkg": "grep-regex 0.1.11 (path+file:///$ROOT$ripgrep/crates/regex)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memmap", - "pkg": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "hermit-abi 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "dependencies": [ - "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "crossbeam_channel", - "pkg": "crossbeam-channel 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "globset", - "pkg": "globset 0.4.10 (path+file:///$ROOT$ripgrep/crates/globset)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "background_threads_runtime_support" - ] - }, - { - "id": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "jemalloc_sys", - "pkg": "jemalloc-sys 0.5.3+5.3.0-patched (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "background_threads_runtime_support", - "default" - ] - }, - { - "id": "jobserver 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - } - ], - "features": [] - }, - { - "id": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std" - ] - }, - { - "id": "memmap2 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(unix)" - } - ] - } - ], - "features": [] - }, - { - "id": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "alloc", - "default", - "race", - "std" - ] - }, - { - "id": "pcre2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pcre2_sys", - "pkg": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "thread_local", - "pkg": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "pcre2-sys 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cc", - "pkg": "cc 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "libc", - "pkg": "libc 0.2.142 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "pkg_config", - "pkg": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "build", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "pkg-config 0.3.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "proc-macro" - ] - }, - { - "id": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "aho_corasick", - "pkg": "aho-corasick 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "memchr", - "pkg": "memchr 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex_syntax", - "pkg": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "aho-corasick", - "default", - "memchr", - "perf", - "perf-cache", - "perf-dfa", - "perf-inline", - "perf-literal", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-automata 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "regex-syntax 0.6.29 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "regex-syntax 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default", - "std", - "unicode", - "unicode-age", - "unicode-bool", - "unicode-case", - "unicode-gencat", - "unicode-perl", - "unicode-script", - "unicode-segment" - ] - }, - { - "id": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)", - "dependencies": [ - "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "bstr", - "pkg": "bstr 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "clap", - "pkg": "clap 2.34.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - }, - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "grep", - "pkg": "grep 0.2.11 (path+file:///$ROOT$ripgrep/crates/grep)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "ignore", - "pkg": "ignore 0.4.20 (path+file:///$ROOT$ripgrep/crates/ignore)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "jemallocator", - "pkg": "jemallocator 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(all(target_env = \"musl\", target_pointer_width = \"64\"))" - } - ] - }, - { - "name": "lazy_static", - "pkg": "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - }, - { - "kind": "build", - "target": null - } - ] - }, - { - "name": "log", - "pkg": "log 0.4.17 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "regex", - "pkg": "regex 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - }, - { - "name": "serde_json", - "pkg": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "termcolor", - "pkg": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "walkdir", - "pkg": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": "dev", - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "serde_derive", - "pkg": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "alloc", - "default", - "derive", - "serde_derive", - "std" - ] - }, - { - "id": "serde_derive 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "syn", - "pkg": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default" - ] - }, - { - "id": "serde_json 1.0.96 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "itoa", - "pkg": "itoa 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "ryu", - "pkg": "ryu 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "serde", - "pkg": "serde 1.0.160 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "default", - "std" - ] - }, - { - "id": "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "syn 2.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "proc_macro2", - "pkg": "proc-macro2 1.0.56 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "quote", - "pkg": "quote 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "unicode_ident", - "pkg": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [ - "clone-impls", - "default", - "derive", - "parsing", - "printing", - "proc-macro", - "quote" - ] - }, - { - "id": "termcolor 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "unicode_width", - "pkg": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "thread_local 1.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "cfg_if", - "pkg": "cfg-if 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "once_cell", - "pkg": "once_cell 1.17.1 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "unicode-ident 1.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "unicode-width 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [ - "default" - ] - }, - { - "id": "walkdir 2.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "same_file", - "pkg": "same-file 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - }, - { - "name": "winapi_util", - "pkg": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi_i686_pc_windows_gnu", - "pkg": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "i686-pc-windows-gnu" - } - ] - }, - { - "name": "winapi_x86_64_pc_windows_gnu", - "pkg": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "x86_64-pc-windows-gnu" - } - ] - } - ], - "features": [ - "consoleapi", - "errhandlingapi", - "fileapi", - "minwinbase", - "minwindef", - "processenv", - "std", - "winbase", - "wincon", - "winerror", - "winnt" - ] - }, - { - "id": "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - }, - { - "id": "winapi-util 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [ - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" - ], - "deps": [ - { - "name": "winapi", - "pkg": "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "dep_kinds": [ - { - "kind": null, - "target": "cfg(windows)" - } - ] - } - ], - "features": [] - }, - { - "id": "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "ripgrep 13.0.0 (path+file:///$ROOT$ripgrep)" - }, - "target_directory": "$ROOT$ripgrep/target", - "version": 1, - "workspace_root": "$ROOT$ripgrep", - "metadata": null -} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs index 21b481c1fa29..41b42573f082 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs @@ -82,6 +82,7 @@ fn actual_main() -> anyhow::Result { flags::RustAnalyzerCmd::Highlight(cmd) => cmd.run()?, flags::RustAnalyzerCmd::AnalysisStats(cmd) => cmd.run(verbosity)?, flags::RustAnalyzerCmd::Diagnostics(cmd) => cmd.run()?, + flags::RustAnalyzerCmd::UnresolvedReferences(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Ssr(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Search(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Lsif(cmd) => cmd.run()?, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs index 5eb6ff664f66..a7ec5af89fcb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli.rs @@ -13,6 +13,7 @@ mod rustc_tests; mod scip; mod ssr; mod symbols; +mod unresolved_references; mod progress_report; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs index 44e56645ba3c..e899e0e8eea5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -64,6 +64,7 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, + all_targets: true, ..Default::default() }; let no_progress = &|_| (); @@ -77,7 +78,11 @@ impl flags::AnalysisStats { let metadata_time = db_load_sw.elapsed(); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: !self.disable_build_scripts, - with_proc_macro_server: ProcMacroServerChoice::Sysroot, + with_proc_macro_server: if self.disable_proc_macros { + ProcMacroServerChoice::None + } else { + ProcMacroServerChoice::Sysroot + }, prefill_caches: false, }; @@ -339,6 +344,7 @@ impl flags::AnalysisStats { true => None, false => Some(RustLibSource::Discover), }, + all_targets: true, ..Default::default() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs index cdac0e5ef5cc..28f25975d64a 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs @@ -24,8 +24,11 @@ impl flags::Diagnostics { handle.join() } fn run_(self) -> anyhow::Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); ProcMacroServerChoice::Explicit(path) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs index 16d90de661ad..73e71658d17c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs @@ -124,6 +124,19 @@ xflags::xflags! { optional --proc-macro-srv path: PathBuf } + /// Report unresolved references + cmd unresolved-references { + /// Directory with Cargo.toml. + required path: PathBuf + + /// Don't run build scripts or load `OUT_DIR` values by running `cargo check` before analysis. + optional --disable-build-scripts + /// Don't use expand proc macros. + optional --disable-proc-macros + /// Run a custom proc-macro-srv binary. + optional --proc-macro-srv path: PathBuf + } + cmd ssr { /// A structured search replace rule (`$a.foo($b) ==>> bar($a, $b)`) repeated rule: SsrRule @@ -181,6 +194,7 @@ pub enum RustAnalyzerCmd { RunTests(RunTests), RustcTests(RustcTests), Diagnostics(Diagnostics), + UnresolvedReferences(UnresolvedReferences), Ssr(Ssr), Search(Search), Lsif(Lsif), @@ -250,6 +264,15 @@ pub struct Diagnostics { pub proc_macro_srv: Option, } +#[derive(Debug)] +pub struct UnresolvedReferences { + pub path: PathBuf, + + pub disable_build_scripts: bool, + pub disable_proc_macros: bool, + pub proc_macro_srv: Option, +} + #[derive(Debug)] pub struct Ssr { pub rule: Vec, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs index 89fe712ced02..e4263a3f667c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs @@ -273,10 +273,12 @@ impl LsifManager<'_> { impl flags::Lsif { pub fn run(self) -> anyhow::Result<()> { - eprintln!("Generating LSIF started..."); let now = Instant::now(); - let cargo_config = - &CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = &CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let no_progress = &|_| (); let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, @@ -285,6 +287,7 @@ impl flags::Lsif { }; let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path)); let root = ProjectManifest::discover_single(&path)?; + eprintln!("Generating LSIF for project at {root}"); let mut workspace = ProjectWorkspace::load(root, cargo_config, no_progress)?; let build_scripts = workspace.run_build_scripts(cargo_config, no_progress)?; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs index 157ef43dd0a2..f90ebcfdb2e3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs @@ -13,8 +13,11 @@ use crate::cli::{flags, full_name_of_item, Result}; impl flags::RunTests { pub fn run(self) -> Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs index 75efdfd7dd8f..730f3c08abb5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs @@ -67,8 +67,11 @@ impl Tester { path.push("ra-rustc-test.rs"); let tmp_file = AbsPathBuf::try_from(Utf8PathBuf::from_path_buf(path).unwrap()).unwrap(); std::fs::write(&tmp_file, "")?; - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env); let data_layout = target_data_layout::get( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs index ceb8534fdf55..e9198977dea5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs @@ -51,7 +51,7 @@ impl flags::Scip { // FIXME @alibektas : What happens to errors without logging? error!(?error_sink, "Config Error(s)"); } - let cargo_config = config.cargo(); + let cargo_config = config.cargo(None); let (db, vfs, _) = load_workspace_at( root.as_path().as_ref(), &cargo_config, @@ -142,7 +142,7 @@ impl flags::Scip { let mut symbol_roles = Default::default(); if let Some(def) = token.definition { - // if the the range of the def and the range of the token are the same, this must be the definition. + // if the range of the def and the range of the token are the same, this must be the definition. // they also must be in the same file. See https://github.com/rust-lang/rust-analyzer/pull/17988 if def.file_id == file_id && def.range == text_range { symbol_roles |= scip_types::SymbolRole::Definition as i32; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs index 3caa48798874..bdca800a0d68 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs @@ -10,8 +10,11 @@ use crate::cli::flags; impl flags::Ssr { pub fn run(self) -> anyhow::Result<()> { - let cargo_config = - CargoConfig { sysroot: Some(RustLibSource::Discover), ..Default::default() }; + let cargo_config = CargoConfig { + sysroot: Some(RustLibSource::Discover), + all_targets: true, + ..Default::default() + }; let load_cargo_config = LoadCargoConfig { load_out_dirs_from_check: true, with_proc_macro_server: ProcMacroServerChoice::Sysroot, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs new file mode 100644 index 000000000000..986bd018b424 --- /dev/null +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs @@ -0,0 +1,175 @@ +//! Reports references in code that the IDE layer cannot resolve. +use hir::{db::HirDatabase, AnyDiagnostic, Crate, HirFileIdExt as _, Module, Semantics}; +use ide::{AnalysisHost, RootDatabase, TextRange}; +use ide_db::{ + base_db::{SourceDatabase, SourceRootDatabase}, + defs::NameRefClass, + EditionedFileId, FxHashSet, LineIndexDatabase as _, +}; +use load_cargo::{load_workspace_at, LoadCargoConfig, ProcMacroServerChoice}; +use parser::SyntaxKind; +use syntax::{ast, AstNode, WalkEvent}; +use vfs::FileId; + +use crate::cli::flags; + +impl flags::UnresolvedReferences { + pub fn run(self) -> anyhow::Result<()> { + const STACK_SIZE: usize = 1024 * 1024 * 8; + + let handle = stdx::thread::Builder::new(stdx::thread::ThreadIntent::LatencySensitive) + .name("BIG_STACK_THREAD".into()) + .stack_size(STACK_SIZE) + .spawn(|| self.run_()) + .unwrap(); + + handle.join() + } + + fn run_(self) -> anyhow::Result<()> { + let root = + vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize(); + let config = crate::config::Config::new( + root.clone(), + lsp_types::ClientCapabilities::default(), + vec![], + None, + ); + let cargo_config = config.cargo(None); + let with_proc_macro_server = if let Some(p) = &self.proc_macro_srv { + let path = vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(p)); + ProcMacroServerChoice::Explicit(path) + } else { + ProcMacroServerChoice::Sysroot + }; + let load_cargo_config = LoadCargoConfig { + load_out_dirs_from_check: !self.disable_build_scripts, + with_proc_macro_server, + prefill_caches: false, + }; + let (db, vfs, _proc_macro) = + load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?; + let host = AnalysisHost::with_database(db); + let db = host.raw_database(); + let sema = Semantics::new(db); + + let mut visited_files = FxHashSet::default(); + + let work = all_modules(db).into_iter().filter(|module| { + let file_id = module.definition_source_file_id(db).original_file(db); + let source_root = db.file_source_root(file_id.into()); + let source_root = db.source_root(source_root); + !source_root.is_library + }); + + for module in work { + let file_id = module.definition_source_file_id(db).original_file(db); + if !visited_files.contains(&file_id) { + let crate_name = + module.krate().display_name(db).as_deref().unwrap_or("unknown").to_owned(); + let file_path = vfs.file_path(file_id.into()); + eprintln!("processing crate: {crate_name}, module: {file_path}",); + + let line_index = db.line_index(file_id.into()); + let file_text = db.file_text(file_id.into()); + + for range in find_unresolved_references(db, &sema, file_id.into(), &module) { + let line_col = line_index.line_col(range.start()); + let line = line_col.line + 1; + let col = line_col.col + 1; + let text = &file_text[range]; + println!("{file_path}:{line}:{col}: {text}"); + } + + visited_files.insert(file_id); + } + } + + eprintln!(); + eprintln!("scan complete"); + + Ok(()) + } +} + +fn all_modules(db: &dyn HirDatabase) -> Vec { + let mut worklist: Vec<_> = + Crate::all(db).into_iter().map(|krate| krate.root_module()).collect(); + let mut modules = Vec::new(); + + while let Some(module) = worklist.pop() { + modules.push(module); + worklist.extend(module.children(db)); + } + + modules +} + +fn find_unresolved_references( + db: &RootDatabase, + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, + module: &Module, +) -> Vec { + let mut unresolved_references = all_unresolved_references(sema, file_id); + + // remove unresolved references which are within inactive code + let mut diagnostics = Vec::new(); + module.diagnostics(db, &mut diagnostics, false); + for diagnostic in diagnostics { + let AnyDiagnostic::InactiveCode(inactive_code) = diagnostic else { + continue; + }; + + let node = inactive_code.node; + let range = node.map(|it| it.text_range()).original_node_file_range_rooted(db); + + if range.file_id != file_id { + continue; + } + + unresolved_references.retain(|r| !range.range.contains_range(*r)); + } + + unresolved_references +} + +fn all_unresolved_references( + sema: &Semantics<'_, RootDatabase>, + file_id: FileId, +) -> Vec { + let file_id = sema + .attach_first_edition(file_id) + .unwrap_or_else(|| EditionedFileId::current_edition(file_id)); + let file = sema.parse(file_id); + let root = file.syntax(); + + let mut unresolved_references = Vec::new(); + for event in root.preorder() { + let WalkEvent::Enter(syntax) = event else { + continue; + }; + let Some(name_ref) = ast::NameRef::cast(syntax) else { + continue; + }; + let Some(descended_name_ref) = name_ref.syntax().first_token().and_then(|tok| { + sema.descend_into_macros_single_exact(tok).parent().and_then(ast::NameRef::cast) + }) else { + continue; + }; + + // if we can classify the name_ref, it's not unresolved + if NameRefClass::classify(sema, &descended_name_ref).is_some() { + continue; + } + + // if we couldn't classify it, but it's in an attr, ignore it. See #10935 + if descended_name_ref.syntax().ancestors().any(|it| it.kind() == SyntaxKind::ATTR) { + continue; + } + + // otherwise, it's unresolved + unresolved_references.push(name_ref.syntax().text_range()); + } + unresolved_references +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 2889af844b14..4cc60695fe66 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -24,7 +24,8 @@ use ide_db::{ use itertools::Itertools; use paths::{Utf8Path, Utf8PathBuf}; use project_model::{ - CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectManifest, RustLibSource, + CargoConfig, CargoFeatures, ProjectJson, ProjectJsonData, ProjectJsonFromCommand, + ProjectManifest, RustLibSource, }; use rustc_hash::{FxHashMap, FxHashSet}; use semver::Version; @@ -59,15 +60,13 @@ mod patch_old_style; // However, editor specific config, which the server doesn't know about, should // be specified directly in `package.json`. // -// To deprecate an option by replacing it with another name use `new_name | `old_name` so that we keep +// To deprecate an option by replacing it with another name use `new_name` | `old_name` so that we keep // parsing the old name. config_data! { - /// Configs that apply on a workspace-wide scope. There are 3 levels on which a global configuration can be configured - // FIXME: 1. and 3. should be split, some configs do not make sense per project + /// Configs that apply on a workspace-wide scope. There are 2 levels on which a global configuration can be configured /// - /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer.toml) + /// 1. `rust-analyzer.toml` file under user's config directory (e.g ~/.config/rust-analyzer/rust-analyzer.toml) /// 2. Client's own configurations (e.g `settings.json` on VS Code) - /// 3. `rust-analyzer.toml` file located at the workspace root /// /// A config is searched for by traversing a "config tree" in a bottom up fashion. It is chosen by the nearest first principle. global: struct GlobalDefaultConfigData <- GlobalConfigInput -> { @@ -76,178 +75,9 @@ config_data! { /// How many worker threads to handle priming caches. The default `0` means to pick automatically. cachePriming_numThreads: NumThreads = NumThreads::Physical, - /// Pass `--all-targets` to cargo invocation. - cargo_allTargets: bool = true, - /// Automatically refresh project info via `cargo metadata` on - /// `Cargo.toml` or `.cargo/config.toml` changes. - pub(crate) cargo_autoreload: bool = true, - /// Run build scripts (`build.rs`) for more precise code analysis. - cargo_buildScripts_enable: bool = true, - /// Specifies the invocation strategy to use when running the build scripts command. - /// If `per_workspace` is set, the command will be executed for each Rust workspace with the - /// workspace as the working directory. - /// If `once` is set, the command will be executed once with the opened project as the - /// working directory. - /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` - /// is set. - cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Override the command rust-analyzer uses to run build scripts and - /// build procedural macros. The command is required to output json - /// and should therefore include `--message-format=json` or a similar - /// option. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. - /// - /// By default, a cargo invocation will be constructed for the configured - /// targets and features, with the following base command line: - /// - /// ```bash - /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going - /// ``` - /// . - cargo_buildScripts_overrideCommand: Option> = None, - /// Rerun proc-macros building/build-scripts running when proc-macro - /// or build-script sources change and are saved. - cargo_buildScripts_rebuildOnSave: bool = true, - /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to - /// avoid checking unnecessary things. - cargo_buildScripts_useRustcWrapper: bool = true, - /// List of cfg options to enable with the given values. - cargo_cfgs: FxHashMap> = { - let mut m = FxHashMap::default(); - m.insert("debug_assertions".to_owned(), None); - m.insert("miri".to_owned(), None); - m - }, - /// Extra arguments that are passed to every cargo invocation. - cargo_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running cargo, rustc - /// or other commands within the workspace. Useful for setting RUSTFLAGS. - cargo_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. - /// - /// Set this to `"all"` to pass `--all-features` to cargo. - cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), - /// Whether to pass `--no-default-features` to cargo. - cargo_noDefaultFeatures: bool = false, - /// Relative path to the sysroot, or "discover" to try to automatically find it via - /// "rustc --print sysroot". - /// - /// Unsetting this disables sysroot loading. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysroot: Option = Some("discover".to_owned()), - /// Relative path to the sysroot library sources. If left unset, this will default to - /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. - /// - /// This option does not take effect until rust-analyzer is restarted. - cargo_sysrootSrc: Option = None, - /// Compilation target override (target triple). - // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work - // than `checkOnSave_target` - cargo_target: Option = None, - /// Optional path to a rust-analyzer specific target directory. - /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro - /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. - /// - /// Set to `true` to use a subdirectory of the existing target directory or - /// set to a path relative to the workspace to use that path. - cargo_targetDir | rust_analyzerTargetDir: Option = None, + /// Custom completion snippets. + completion_snippets_custom: FxHashMap = Config::completion_snippets_default(), - /// Run the check command for diagnostics on save. - checkOnSave | checkOnSave_enable: bool = true, - - /// Check all targets and tests (`--all-targets`). Defaults to - /// `#rust-analyzer.cargo.allTargets#`. - check_allTargets | checkOnSave_allTargets: Option = None, - /// Cargo command to use for `cargo check`. - check_command | checkOnSave_command: String = "check".to_owned(), - /// Extra arguments for `cargo check`. - check_extraArgs | checkOnSave_extraArgs: Vec = vec![], - /// Extra environment variables that will be set when running `cargo check`. - /// Extends `#rust-analyzer.cargo.extraEnv#`. - check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), - /// List of features to activate. Defaults to - /// `#rust-analyzer.cargo.features#`. - /// - /// Set to `"all"` to pass `--all-features` to Cargo. - check_features | checkOnSave_features: Option = None, - /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. - /// - /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... - check_ignore: FxHashSet = FxHashSet::default(), - /// Specifies the invocation strategy to use when running the check command. - /// If `per_workspace` is set, the command will be executed for each workspace. - /// If `once` is set, the command will be executed once. - /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` - /// is set. - check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, - /// Whether to pass `--no-default-features` to Cargo. Defaults to - /// `#rust-analyzer.cargo.noDefaultFeatures#`. - check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, - /// Override the command rust-analyzer uses instead of `cargo check` for - /// diagnostics on save. The command is required to output json and - /// should therefore include `--message-format=json` or a similar option - /// (if your client supports the `colorDiagnosticOutput` experimental - /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). - /// - /// If you're changing this because you're using some tool wrapping - /// Cargo, you might also want to change - /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. - /// - /// If there are multiple linked projects/workspaces, this command is invoked for - /// each of them, with the working directory being the workspace root - /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten - /// by changing `#rust-analyzer.check.invocationStrategy#`. - /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. - /// - /// An example command would be: - /// - /// ```bash - /// cargo check --workspace --message-format=json --all-targets - /// ``` - /// . - check_overrideCommand | checkOnSave_overrideCommand: Option> = None, - /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. - /// - /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. - /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. - /// - /// Aliased as `"checkOnSave.targets"`. - check_targets | checkOnSave_targets | checkOnSave_target: Option = None, - /// Whether `--workspace` should be passed to `cargo check`. - /// If false, `-p ` will be passed instead. - check_workspace: bool = true, - - /// List of rust-analyzer diagnostics to disable. - diagnostics_disabled: FxHashSet = FxHashSet::default(), - /// Whether to show native rust-analyzer diagnostics. - diagnostics_enable: bool = true, - /// Whether to show experimental rust-analyzer diagnostics that might - /// have more false positives than usual. - diagnostics_experimental_enable: bool = false, - /// Map of prefixes to be substituted when parsing diagnostic file paths. - /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. - diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), - /// Whether to run additional style lints. - diagnostics_styleLints_enable: bool = false, - /// List of warnings that should be displayed with hint severity. - /// - /// The warnings will be indicated by faded text or three dots in code - /// and will not show up in the `Problems Panel`. - diagnostics_warningsAsHint: Vec = vec![], - /// List of warnings that should be displayed with info severity. - /// - /// The warnings will be indicated by a blue squiggly underline in code - /// and a blue icon in the `Problems Panel`. - diagnostics_warningsAsInfo: Vec = vec![], /// These directories will be ignored by rust-analyzer. They are /// relative to the workspace root, and globs are not supported. You may @@ -255,266 +85,6 @@ config_data! { files_excludeDirs: Vec = vec![], - /// Disable project auto-discovery in favor of explicitly specified set - /// of projects. - /// - /// Elements must be paths pointing to `Cargo.toml`, - /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON - /// objects in `rust-project.json` format. - linkedProjects: Vec = vec![], - - /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. - lru_capacity: Option = None, - /// Sets the LRU capacity of the specified queries. - lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), - - /// These proc-macros will be ignored when trying to expand them. - /// - /// This config takes a map of crate names with the exported proc-macro names to ignore as values. - procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), - - /// Command to be executed instead of 'cargo' for runnables. - runnables_command: Option = None, - /// Additional arguments to be passed to cargo for runnables such as - /// tests or binaries. For example, it may be `--release`. - runnables_extraArgs: Vec = vec![], - /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or - /// doc-tests. - /// - /// Unless the launched target uses a - /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), - /// they will end up being interpreted as options to - /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). - runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], - - /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private - /// projects, or "discover" to try to automatically find it if the `rustc-dev` component - /// is installed. - /// - /// Any project which uses rust-analyzer with the rustcPrivate - /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. - /// - /// This option does not take effect until rust-analyzer is restarted. - rustc_source: Option = None, - - - /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - /// - /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. - /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch` - /// is used to determine which build system-specific files should be watched in order to - /// reload rust-analyzer. - /// - /// Below is an example of a valid configuration: - /// ```json - /// "rust-analyzer.workspace.discoverConfig": { - /// "command": [ - /// "rust-project", - /// "develop-json" - /// ], - /// "progressLabel": "rust-analyzer", - /// "filesToWatch": [ - /// "BUCK" - /// ] - /// } - /// ``` - /// - /// ## On `DiscoverWorkspaceConfig::command` - /// - /// **Warning**: This format is provisional and subject to change. - /// - /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object - /// corresponding to `DiscoverProjectData::Finished`: - /// - /// ```norun - /// #[derive(Debug, Clone, Deserialize, Serialize)] - /// #[serde(tag = "kind")] - /// #[serde(rename_all = "snake_case")] - /// enum DiscoverProjectData { - /// Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, - /// Error { error: String, source: Option }, - /// Progress { message: String }, - /// } - /// ``` - /// - /// As JSON, `DiscoverProjectData::Finished` is: - /// - /// ```json - /// { - /// // the internally-tagged representation of the enum. - /// "kind": "finished", - /// // the file used by a non-Cargo build system to define - /// // a package or target. - /// "buildfile": "rust-analyzer/BUILD", - /// // the contents of a rust-project.json, elided for brevity - /// "project": { - /// "sysroot": "foo", - /// "crates": [] - /// } - /// } - /// ``` - /// - /// It is encouraged, but not required, to use the other variants on - /// `DiscoverProjectData` to provide a more polished end-user experience. - /// - /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, - /// which will be substituted with the JSON-serialized form of the following - /// enum: - /// - /// ```norun - /// #[derive(PartialEq, Clone, Debug, Serialize)] - /// #[serde(rename_all = "camelCase")] - /// pub enum DiscoverArgument { - /// Path(AbsPathBuf), - /// Buildfile(AbsPathBuf), - /// } - /// ``` - /// - /// The JSON representation of `DiscoverArgument::Path` is: - /// - /// ```json - /// { - /// "path": "src/main.rs" - /// } - /// ``` - /// - /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: - /// - /// ``` - /// { - /// "buildfile": "BUILD" - /// } - /// ``` - /// - /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, - /// and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to - /// to update an existing workspace. As a reference for implementors, - /// buck2's `rust-project` will likely be useful: - /// https://github.com/facebook/buck2/tree/main/integrations/rust-project. - workspace_discoverConfig: Option = None, - } -} - -config_data! { - /// Local configurations can be defined per `SourceRoot`. This almost always corresponds to a `Crate`. - local: struct LocalDefaultConfigData <- LocalConfigInput -> { - /// Whether to insert #[must_use] when generating `as_` methods - /// for enum variants. - assist_emitMustUse: bool = false, - /// Placeholder expression to use for missing expressions in assists. - assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo, - /// Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. - assist_termSearch_borrowcheck: bool = true, - /// Term search fuel in "units of work" for assists (Defaults to 1800). - assist_termSearch_fuel: usize = 1800, - - /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. - imports_granularity_enforce: bool = false, - /// How imports should be grouped into use statements. - imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, - /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. - imports_group_enable: bool = true, - /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. - imports_merge_glob: bool = true, - /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. - imports_preferNoStd | imports_prefer_no_std: bool = false, - /// Whether to prefer import paths containing a `prelude` module. - imports_preferPrelude: bool = false, - /// The path structure for newly inserted paths to use. - imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain, - /// Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". - imports_prefixExternPrelude: bool = false, - } -} - -config_data! { - workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { - - /// Additional arguments to `rustfmt`. - rustfmt_extraArgs: Vec = vec![], - /// Advanced option, fully override the command rust-analyzer uses for - /// formatting. This should be the equivalent of `rustfmt` here, and - /// not that of `cargo fmt`. The file contents will be passed on the - /// standard input and the formatted result will be read from the - /// standard output. - rustfmt_overrideCommand: Option> = None, - /// Enables the use of rustfmt's unstable range formatting command for the - /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only - /// available on a nightly build. - rustfmt_rangeFormatting_enable: bool = false, - - } -} - -config_data! { - /// Configs that only make sense when they are set by a client. As such they can only be defined - /// by setting them using client's settings (e.g `settings.json` on VS Code). - client: struct ClientDefaultConfigData <- ClientConfigInput -> { - /// Toggles the additional completions that automatically add imports when completed. - /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. - completion_autoimport_enable: bool = true, - /// Toggles the additional completions that automatically show method calls and field accesses - /// with `self` prefixed to them when inside a method. - completion_autoself_enable: bool = true, - /// Whether to add parenthesis and argument snippets when completing function. - completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, - /// Whether to show full function/method signatures in completion docs. - completion_fullFunctionSignatures_enable: bool = false, - /// Maximum number of completions to return. If `None`, the limit is infinite. - completion_limit: Option = None, - /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. - completion_postfix_enable: bool = true, - /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. - completion_privateEditable_enable: bool = false, - /// Custom completion snippets. - completion_snippets_custom: FxHashMap = serde_json::from_str(r#"{ - "Arc::new": { - "postfix": "arc", - "body": "Arc::new(${receiver})", - "requires": "std::sync::Arc", - "description": "Put the expression into an `Arc`", - "scope": "expr" - }, - "Rc::new": { - "postfix": "rc", - "body": "Rc::new(${receiver})", - "requires": "std::rc::Rc", - "description": "Put the expression into an `Rc`", - "scope": "expr" - }, - "Box::pin": { - "postfix": "pinbox", - "body": "Box::pin(${receiver})", - "requires": "std::boxed::Box", - "description": "Put the expression into a pinned `Box`", - "scope": "expr" - }, - "Ok": { - "postfix": "ok", - "body": "Ok(${receiver})", - "description": "Wrap the expression in a `Result::Ok`", - "scope": "expr" - }, - "Err": { - "postfix": "err", - "body": "Err(${receiver})", - "description": "Wrap the expression in a `Result::Err`", - "scope": "expr" - }, - "Some": { - "postfix": "some", - "body": "Some(${receiver})", - "description": "Wrap the expression in an `Option::Some`", - "scope": "expr" - } - }"#).unwrap(), - /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. - completion_termSearch_enable: bool = false, - /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). - completion_termSearch_fuel: usize = 1000, - - /// Controls file watching implementation. - files_watcher: FilesWatcherDef = FilesWatcherDef::Client, /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. highlightRelated_breakPoints_enable: bool = true, @@ -663,6 +233,19 @@ config_data! { /// `#rust-analyzer.lens.enable#` is set. lens_run_enable: bool = true, + /// Disable project auto-discovery in favor of explicitly specified set + /// of projects. + /// + /// Elements must be paths pointing to `Cargo.toml`, + /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON + /// objects in `rust-project.json` format. + linkedProjects: Vec = vec![], + + /// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128. + lru_capacity: Option = None, + /// Sets the LRU capacity of the specified queries. + lru_query_capacities: FxHashMap, u16> = FxHashMap::default(), + /// Whether to show `can't find Cargo.toml` error message. notifications_cargoTomlNotFound: bool = true, @@ -727,6 +310,383 @@ config_data! { /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. typing_autoClosingAngleBrackets_enable: bool = false, + + /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. + /// + /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. + /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch` + /// is used to determine which build system-specific files should be watched in order to + /// reload rust-analyzer. + /// + /// Below is an example of a valid configuration: + /// ```json + /// "rust-analyzer.workspace.discoverConfig": { + /// "command": [ + /// "rust-project", + /// "develop-json" + /// ], + /// "progressLabel": "rust-analyzer", + /// "filesToWatch": [ + /// "BUCK" + /// ] + /// } + /// ``` + /// + /// ## On `DiscoverWorkspaceConfig::command` + /// + /// **Warning**: This format is provisional and subject to change. + /// + /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object + /// corresponding to `DiscoverProjectData::Finished`: + /// + /// ```norun + /// #[derive(Debug, Clone, Deserialize, Serialize)] + /// #[serde(tag = "kind")] + /// #[serde(rename_all = "snake_case")] + /// enum DiscoverProjectData { + /// Finished { buildfile: Utf8PathBuf, project: ProjectJsonData }, + /// Error { error: String, source: Option }, + /// Progress { message: String }, + /// } + /// ``` + /// + /// As JSON, `DiscoverProjectData::Finished` is: + /// + /// ```json + /// { + /// // the internally-tagged representation of the enum. + /// "kind": "finished", + /// // the file used by a non-Cargo build system to define + /// // a package or target. + /// "buildfile": "rust-analyzer/BUILD", + /// // the contents of a rust-project.json, elided for brevity + /// "project": { + /// "sysroot": "foo", + /// "crates": [] + /// } + /// } + /// ``` + /// + /// It is encouraged, but not required, to use the other variants on + /// `DiscoverProjectData` to provide a more polished end-user experience. + /// + /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, + /// which will be substituted with the JSON-serialized form of the following + /// enum: + /// + /// ```norun + /// #[derive(PartialEq, Clone, Debug, Serialize)] + /// #[serde(rename_all = "camelCase")] + /// pub enum DiscoverArgument { + /// Path(AbsPathBuf), + /// Buildfile(AbsPathBuf), + /// } + /// ``` + /// + /// The JSON representation of `DiscoverArgument::Path` is: + /// + /// ```json + /// { + /// "path": "src/main.rs" + /// } + /// ``` + /// + /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is: + /// + /// ``` + /// { + /// "buildfile": "BUILD" + /// } + /// ``` + /// + /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, + /// and therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to + /// to update an existing workspace. As a reference for implementors, + /// buck2's `rust-project` will likely be useful: + /// https://github.com/facebook/buck2/tree/main/integrations/rust-project. + workspace_discoverConfig: Option = None, + } +} + +config_data! { + /// Local configurations can be defined per `SourceRoot`. This almost always corresponds to a `Crate`. + local: struct LocalDefaultConfigData <- LocalConfigInput -> { + /// Whether to insert #[must_use] when generating `as_` methods + /// for enum variants. + assist_emitMustUse: bool = false, + /// Placeholder expression to use for missing expressions in assists. + assist_expressionFillDefault: ExprFillDefaultDef = ExprFillDefaultDef::Todo, + /// Enable borrow checking for term search code assists. If set to false, also there will be more suggestions, but some of them may not borrow-check. + assist_termSearch_borrowcheck: bool = true, + /// Term search fuel in "units of work" for assists (Defaults to 1800). + assist_termSearch_fuel: usize = 1800, + + + /// Whether to automatically add a semicolon when completing unit-returning functions. + /// + /// In `match` arms it completes a comma instead. + completion_addSemicolonToUnit: bool = true, + /// Toggles the additional completions that automatically add imports when completed. + /// Note that your client must specify the `additionalTextEdits` LSP client capability to truly have this feature enabled. + completion_autoimport_enable: bool = true, + /// Toggles the additional completions that automatically show method calls and field accesses + /// with `self` prefixed to them when inside a method. + completion_autoself_enable: bool = true, + /// Whether to add parenthesis and argument snippets when completing function. + completion_callable_snippets: CallableCompletionDef = CallableCompletionDef::FillArguments, + /// Whether to show full function/method signatures in completion docs. + completion_fullFunctionSignatures_enable: bool = false, + /// Whether to omit deprecated items from autocompletion. By default they are marked as deprecated but not hidden. + completion_hideDeprecated: bool = false, + /// Maximum number of completions to return. If `None`, the limit is infinite. + completion_limit: Option = None, + /// Whether to show postfix snippets like `dbg`, `if`, `not`, etc. + completion_postfix_enable: bool = true, + /// Enables completions of private items and fields that are defined in the current workspace even if they are not visible at the current position. + completion_privateEditable_enable: bool = false, + /// Whether to enable term search based snippets like `Some(foo.bar().baz())`. + completion_termSearch_enable: bool = false, + /// Term search fuel in "units of work" for autocompletion (Defaults to 1000). + completion_termSearch_fuel: usize = 1000, + + /// List of rust-analyzer diagnostics to disable. + diagnostics_disabled: FxHashSet = FxHashSet::default(), + /// Whether to show native rust-analyzer diagnostics. + diagnostics_enable: bool = true, + /// Whether to show experimental rust-analyzer diagnostics that might + /// have more false positives than usual. + diagnostics_experimental_enable: bool = false, + /// Map of prefixes to be substituted when parsing diagnostic file paths. + /// This should be the reverse mapping of what is passed to `rustc` as `--remap-path-prefix`. + diagnostics_remapPrefix: FxHashMap = FxHashMap::default(), + /// Whether to run additional style lints. + diagnostics_styleLints_enable: bool = false, + /// List of warnings that should be displayed with hint severity. + /// + /// The warnings will be indicated by faded text or three dots in code + /// and will not show up in the `Problems Panel`. + diagnostics_warningsAsHint: Vec = vec![], + /// List of warnings that should be displayed with info severity. + /// + /// The warnings will be indicated by a blue squiggly underline in code + /// and a blue icon in the `Problems Panel`. + diagnostics_warningsAsInfo: Vec = vec![], + + /// Whether to enforce the import granularity setting for all files. If set to false rust-analyzer will try to keep import styles consistent per file. + imports_granularity_enforce: bool = false, + /// How imports should be grouped into use statements. + imports_granularity_group: ImportGranularityDef = ImportGranularityDef::Crate, + /// Group inserted imports by the https://rust-analyzer.github.io/manual.html#auto-import[following order]. Groups are separated by newlines. + imports_group_enable: bool = true, + /// Whether to allow import insertion to merge new imports into single path glob imports like `use std::fmt::*;`. + imports_merge_glob: bool = true, + /// Prefer to unconditionally use imports of the core and alloc crate, over the std crate. + imports_preferNoStd | imports_prefer_no_std: bool = false, + /// Whether to prefer import paths containing a `prelude` module. + imports_preferPrelude: bool = false, + /// The path structure for newly inserted paths to use. + imports_prefix: ImportPrefixDef = ImportPrefixDef::Plain, + /// Whether to prefix external (including std, core) crate imports with `::`. e.g. "use ::std::io::Read;". + imports_prefixExternPrelude: bool = false, + } +} + +config_data! { + workspace: struct WorkspaceDefaultConfigData <- WorkspaceConfigInput -> { + /// Pass `--all-targets` to cargo invocation. + cargo_allTargets: bool = true, + /// Automatically refresh project info via `cargo metadata` on + /// `Cargo.toml` or `.cargo/config.toml` changes. + cargo_autoreload: bool = true, + /// Run build scripts (`build.rs`) for more precise code analysis. + cargo_buildScripts_enable: bool = true, + /// Specifies the invocation strategy to use when running the build scripts command. + /// If `per_workspace` is set, the command will be executed for each Rust workspace with the + /// workspace as the working directory. + /// If `once` is set, the command will be executed once with the opened project as the + /// working directory. + /// This config only has an effect when `#rust-analyzer.cargo.buildScripts.overrideCommand#` + /// is set. + cargo_buildScripts_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Override the command rust-analyzer uses to run build scripts and + /// build procedural macros. The command is required to output json + /// and should therefore include `--message-format=json` or a similar + /// option. + /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`. + /// + /// By default, a cargo invocation will be constructed for the configured + /// targets and features, with the following base command line: + /// + /// ```bash + /// cargo check --quiet --workspace --message-format=json --all-targets --keep-going + /// ``` + /// . + cargo_buildScripts_overrideCommand: Option> = None, + /// Rerun proc-macros building/build-scripts running when proc-macro + /// or build-script sources change and are saved. + cargo_buildScripts_rebuildOnSave: bool = true, + /// Use `RUSTC_WRAPPER=rust-analyzer` when running build scripts to + /// avoid checking unnecessary things. + cargo_buildScripts_useRustcWrapper: bool = true, + /// List of cfg options to enable with the given values. + cargo_cfgs: FxHashMap> = { + let mut m = FxHashMap::default(); + m.insert("debug_assertions".to_owned(), None); + m.insert("miri".to_owned(), None); + m + }, + /// Extra arguments that are passed to every cargo invocation. + cargo_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running cargo, rustc + /// or other commands within the workspace. Useful for setting RUSTFLAGS. + cargo_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. + /// + /// Set this to `"all"` to pass `--all-features` to cargo. + cargo_features: CargoFeaturesDef = CargoFeaturesDef::Selected(vec![]), + /// Whether to pass `--no-default-features` to cargo. + cargo_noDefaultFeatures: bool = false, + /// Relative path to the sysroot, or "discover" to try to automatically find it via + /// "rustc --print sysroot". + /// + /// Unsetting this disables sysroot loading. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysroot: Option = Some("discover".to_owned()), + /// Relative path to the sysroot library sources. If left unset, this will default to + /// `{cargo.sysroot}/lib/rustlib/src/rust/library`. + /// + /// This option does not take effect until rust-analyzer is restarted. + cargo_sysrootSrc: Option = None, + /// Compilation target override (target triple). + // FIXME(@poliorcetics): move to multiple targets here too, but this will need more work + // than `checkOnSave_target` + cargo_target: Option = None, + /// Optional path to a rust-analyzer specific target directory. + /// This prevents rust-analyzer's `cargo check` and initial build-script and proc-macro + /// building from locking the `Cargo.lock` at the expense of duplicating build artifacts. + /// + /// Set to `true` to use a subdirectory of the existing target directory or + /// set to a path relative to the workspace to use that path. + cargo_targetDir | rust_analyzerTargetDir: Option = None, + + /// Run the check command for diagnostics on save. + checkOnSave | checkOnSave_enable: bool = true, + + + /// Check all targets and tests (`--all-targets`). Defaults to + /// `#rust-analyzer.cargo.allTargets#`. + check_allTargets | checkOnSave_allTargets: Option = None, + /// Cargo command to use for `cargo check`. + check_command | checkOnSave_command: String = "check".to_owned(), + /// Extra arguments for `cargo check`. + check_extraArgs | checkOnSave_extraArgs: Vec = vec![], + /// Extra environment variables that will be set when running `cargo check`. + /// Extends `#rust-analyzer.cargo.extraEnv#`. + check_extraEnv | checkOnSave_extraEnv: FxHashMap = FxHashMap::default(), + /// List of features to activate. Defaults to + /// `#rust-analyzer.cargo.features#`. + /// + /// Set to `"all"` to pass `--all-features` to Cargo. + check_features | checkOnSave_features: Option = None, + /// List of `cargo check` (or other command specified in `check.command`) diagnostics to ignore. + /// + /// For example for `cargo check`: `dead_code`, `unused_imports`, `unused_variables`,... + check_ignore: FxHashSet = FxHashSet::default(), + /// Specifies the invocation strategy to use when running the check command. + /// If `per_workspace` is set, the command will be executed for each workspace. + /// If `once` is set, the command will be executed once. + /// This config only has an effect when `#rust-analyzer.check.overrideCommand#` + /// is set. + check_invocationStrategy | checkOnSave_invocationStrategy: InvocationStrategy = InvocationStrategy::PerWorkspace, + /// Whether to pass `--no-default-features` to Cargo. Defaults to + /// `#rust-analyzer.cargo.noDefaultFeatures#`. + check_noDefaultFeatures | checkOnSave_noDefaultFeatures: Option = None, + /// Override the command rust-analyzer uses instead of `cargo check` for + /// diagnostics on save. The command is required to output json and + /// should therefore include `--message-format=json` or a similar option + /// (if your client supports the `colorDiagnosticOutput` experimental + /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). + /// + /// If you're changing this because you're using some tool wrapping + /// Cargo, you might also want to change + /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. + /// + /// If there are multiple linked projects/workspaces, this command is invoked for + /// each of them, with the working directory being the workspace root + /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten + /// by changing `#rust-analyzer.check.invocationStrategy#`. + /// + /// If `$saved_file` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. This is + /// intended to be used with non-Cargo build systems. + /// Note that `$saved_file` is experimental and may be removed in the future. + /// + /// An example command would be: + /// + /// ```bash + /// cargo check --workspace --message-format=json --all-targets + /// ``` + /// . + check_overrideCommand | checkOnSave_overrideCommand: Option> = None, + /// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty. + /// + /// Can be a single target, e.g. `"x86_64-unknown-linux-gnu"` or a list of targets, e.g. + /// `["aarch64-apple-darwin", "x86_64-apple-darwin"]`. + /// + /// Aliased as `"checkOnSave.targets"`. + check_targets | checkOnSave_targets | checkOnSave_target: Option = None, + /// Whether `--workspace` should be passed to `cargo check`. + /// If false, `-p ` will be passed instead. + check_workspace: bool = true, + + /// These proc-macros will be ignored when trying to expand them. + /// + /// This config takes a map of crate names with the exported proc-macro names to ignore as values. + procMacro_ignored: FxHashMap, Box<[Box]>> = FxHashMap::default(), + + /// Command to be executed instead of 'cargo' for runnables. + runnables_command: Option = None, + /// Additional arguments to be passed to cargo for runnables such as + /// tests or binaries. For example, it may be `--release`. + runnables_extraArgs: Vec = vec![], + /// Additional arguments to be passed through Cargo to launched tests, benchmarks, or + /// doc-tests. + /// + /// Unless the launched target uses a + /// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field), + /// they will end up being interpreted as options to + /// [`rustc`’s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments). + runnables_extraTestBinaryArgs: Vec = vec!["--show-output".to_owned()], + + /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private + /// projects, or "discover" to try to automatically find it if the `rustc-dev` component + /// is installed. + /// + /// Any project which uses rust-analyzer with the rustcPrivate + /// crates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it. + /// + /// This option does not take effect until rust-analyzer is restarted. + rustc_source: Option = None, + + /// Additional arguments to `rustfmt`. + rustfmt_extraArgs: Vec = vec![], + /// Advanced option, fully override the command rust-analyzer uses for + /// formatting. This should be the equivalent of `rustfmt` here, and + /// not that of `cargo fmt`. The file contents will be passed on the + /// standard input and the formatted result will be read from the + /// standard output. + rustfmt_overrideCommand: Option> = None, + /// Enables the use of rustfmt's unstable range formatting command for the + /// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only + /// available on a nightly build. + rustfmt_rangeFormatting_enable: bool = false, + + /// Workspace symbol search kind. workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, /// Limits the number of items returned from a workspace symbol search (Defaults to 128). @@ -735,6 +695,19 @@ config_data! { workspace_symbol_search_limit: usize = 128, /// Workspace symbol search scope. workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace, + + } +} + +config_data! { + /// Configs that only make sense when they are set by a client. As such they can only be defined + /// by setting them using client's settings (e.g `settings.json` on VS Code). + client: struct ClientDefaultConfigData <- ClientConfigInput -> { + + /// Controls file watching implementation. + files_watcher: FilesWatcherDef = FilesWatcherDef::Client, + + } } @@ -753,7 +726,13 @@ enum RatomlFile { #[derive(Debug, Clone)] pub struct Config { - discovered_projects: Vec, + /// Projects that have a Cargo.toml or a rust-project.json in a + /// parent directory, so we can discover them by walking the + /// file system. + discovered_projects_from_filesystem: Vec, + /// Projects whose configuration was generated by a command + /// configured in discoverConfig. + discovered_projects_from_command: Vec, /// The workspace roots as registered by the LSP client workspace_roots: Vec, caps: ClientCapabilities, @@ -1016,7 +995,7 @@ impl Config { config.source_root_parent_map = source_root_map; } - if config.check_command().is_empty() { + if config.check_command(None).is_empty() { config.validation_errors.0.push(Arc::new(ConfigErrorInner::Json { config_key: "/check/command".to_owned(), error: serde_json::Error::custom("expected a non-empty string"), @@ -1046,19 +1025,19 @@ impl Config { (config, e, should_update) } - pub fn add_linked_projects(&mut self, data: ProjectJsonData, buildfile: AbsPathBuf) { - let linked_projects = &mut self.client_config.0.global.linkedProjects; - - let new_project = ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile }; - match linked_projects { - Some(projects) => { - match projects.iter_mut().find(|p| p.manifest() == new_project.manifest()) { - Some(p) => *p = new_project, - None => projects.push(new_project), - } + pub fn add_discovered_project_from_command( + &mut self, + data: ProjectJsonData, + buildfile: AbsPathBuf, + ) { + for proj in self.discovered_projects_from_command.iter_mut() { + if proj.buildfile == buildfile { + proj.data = data; + return; } - None => *linked_projects = Some(vec![new_project]), } + + self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile }); } } @@ -1251,7 +1230,7 @@ pub struct NotificationsConfig { pub cargo_toml_not_found: bool, } -#[derive(Debug, Clone)] +#[derive(Deserialize, Serialize, Debug, Clone)] pub enum RustfmtConfig { Rustfmt { extra_args: Vec, enable_range_formatting: bool }, CustomCommand { command: String, args: Vec }, @@ -1336,7 +1315,8 @@ impl Config { Config { caps: ClientCapabilities::new(caps), - discovered_projects: Vec::new(), + discovered_projects_from_filesystem: Vec::new(), + discovered_projects_from_command: Vec::new(), root_path, snippets: Default::default(), workspace_roots, @@ -1357,7 +1337,7 @@ impl Config { if discovered.is_empty() { tracing::error!("failed to find any projects in {:?}", &self.workspace_roots); } - self.discovered_projects = discovered; + self.discovered_projects_from_filesystem = discovered; } pub fn remove_workspace(&mut self, path: &AbsPath) { @@ -1412,29 +1392,36 @@ impl Config { pub fn completion(&self, source_root: Option) -> CompletionConfig { CompletionConfig { - enable_postfix_completions: self.completion_postfix_enable().to_owned(), - enable_imports_on_the_fly: self.completion_autoimport_enable().to_owned() + enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(), + enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned() && self.caps.completion_item_edit_resolve(), - enable_self_on_the_fly: self.completion_autoself_enable().to_owned(), - enable_private_editable: self.completion_privateEditable_enable().to_owned(), - full_function_signatures: self.completion_fullFunctionSignatures_enable().to_owned(), - callable: match self.completion_callable_snippets() { + enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(), + enable_private_editable: self.completion_privateEditable_enable(source_root).to_owned(), + full_function_signatures: self + .completion_fullFunctionSignatures_enable(source_root) + .to_owned(), + callable: match self.completion_callable_snippets(source_root) { CallableCompletionDef::FillArguments => Some(CallableSnippets::FillArguments), CallableCompletionDef::AddParentheses => Some(CallableSnippets::AddParentheses), CallableCompletionDef::None => None, }, + add_semicolon_to_unit: *self.completion_addSemicolonToUnit(source_root), snippet_cap: SnippetCap::new(self.completion_snippet()), insert_use: self.insert_use_config(source_root), prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), snippets: self.snippets.clone().to_vec(), - limit: self.completion_limit().to_owned(), - enable_term_search: self.completion_termSearch_enable().to_owned(), - term_search_fuel: self.completion_termSearch_fuel().to_owned() as u64, + limit: self.completion_limit(source_root).to_owned(), + enable_term_search: self.completion_termSearch_enable(source_root).to_owned(), + term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64, } } + pub fn completion_hide_deprecated(&self) -> bool { + *self.completion_hideDeprecated(None) + } + pub fn detached_files(&self) -> &Vec { // FIXME @alibektas : This is the only config that is confusing. If it's a proper configuration // why is it not among the others? If it's client only which I doubt it is current state should be alright @@ -1443,11 +1430,11 @@ impl Config { pub fn diagnostics(&self, source_root: Option) -> DiagnosticsConfig { DiagnosticsConfig { - enabled: *self.diagnostics_enable(), + enabled: *self.diagnostics_enable(source_root), proc_attr_macros_enabled: self.expand_proc_attr_macros(), proc_macros_enabled: *self.procMacro_enable(), - disable_experimental: !self.diagnostics_experimental_enable(), - disabled: self.diagnostics_disabled().clone(), + disable_experimental: !self.diagnostics_experimental_enable(source_root), + disabled: self.diagnostics_disabled(source_root).clone(), expr_fill_default: match self.assist_expressionFillDefault(source_root) { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, @@ -1457,7 +1444,7 @@ impl Config { prefer_no_std: self.imports_preferNoStd(source_root).to_owned(), prefer_prelude: self.imports_preferPrelude(source_root).to_owned(), prefer_absolute: self.imports_prefixExternPrelude(source_root).to_owned(), - style_lints: self.diagnostics_styleLints_enable().to_owned(), + style_lints: self.diagnostics_styleLints_enable(source_root).to_owned(), term_search_fuel: self.assist_termSearch_fuel(source_root).to_owned() as u64, term_search_borrowck: self.assist_termSearch_borrowcheck(source_root).to_owned(), } @@ -1673,78 +1660,95 @@ impl Config { self.workspace_discoverConfig().as_ref() } - pub fn linked_or_discovered_projects(&self) -> Vec { - match self.linkedProjects().as_slice() { - [] => { - let exclude_dirs: Vec<_> = - self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); - self.discovered_projects - .iter() - .filter(|project| { - !exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p)) - }) - .cloned() - .map(LinkedProject::from) - .collect() - } - linked_projects => linked_projects - .iter() - .filter_map(|linked_project| match linked_project { - ManifestOrProjectJson::Manifest(it) => { - let path = self.root_path.join(it); - ProjectManifest::from_manifest_file(path) - .map_err(|e| tracing::error!("failed to load linked project: {}", e)) - .ok() - .map(Into::into) - } - ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile } => { - let root_path = - buildfile.parent().expect("Unable to get parent of buildfile"); + fn discovered_projects(&self) -> Vec { + let exclude_dirs: Vec<_> = + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect(); - Some(ProjectJson::new(None, root_path, data.clone()).into()) - } - ManifestOrProjectJson::ProjectJson(it) => { - Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) - } - }) - .collect(), + let mut projects = vec![]; + for fs_proj in &self.discovered_projects_from_filesystem { + let manifest_path = fs_proj.manifest_path(); + if exclude_dirs.iter().any(|p| manifest_path.starts_with(p)) { + continue; + } + + let buf: Utf8PathBuf = manifest_path.to_path_buf().into(); + projects.push(ManifestOrProjectJson::Manifest(buf)); } + + for dis_proj in &self.discovered_projects_from_command { + projects.push(ManifestOrProjectJson::DiscoveredProjectJson { + data: dis_proj.data.clone(), + buildfile: dis_proj.buildfile.clone(), + }); + } + + projects + } + + pub fn linked_or_discovered_projects(&self) -> Vec { + let linked_projects = self.linkedProjects(); + let projects = if linked_projects.is_empty() { + self.discovered_projects() + } else { + linked_projects.clone() + }; + + projects + .iter() + .filter_map(|linked_project| match linked_project { + ManifestOrProjectJson::Manifest(it) => { + let path = self.root_path.join(it); + ProjectManifest::from_manifest_file(path) + .map_err(|e| tracing::error!("failed to load linked project: {}", e)) + .ok() + .map(Into::into) + } + ManifestOrProjectJson::DiscoveredProjectJson { data, buildfile } => { + let root_path = buildfile.parent().expect("Unable to get parent of buildfile"); + + Some(ProjectJson::new(None, root_path, data.clone()).into()) + } + ManifestOrProjectJson::ProjectJson(it) => { + Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) + } + }) + .collect() } pub fn prefill_caches(&self) -> bool { self.cachePriming_enable().to_owned() } - pub fn publish_diagnostics(&self) -> bool { - self.diagnostics_enable().to_owned() + pub fn publish_diagnostics(&self, source_root: Option) -> bool { + self.diagnostics_enable(source_root).to_owned() } - pub fn diagnostics_map(&self) -> DiagnosticsMapConfig { + pub fn diagnostics_map(&self, source_root: Option) -> DiagnosticsMapConfig { DiagnosticsMapConfig { - remap_prefix: self.diagnostics_remapPrefix().clone(), - warnings_as_info: self.diagnostics_warningsAsInfo().clone(), - warnings_as_hint: self.diagnostics_warningsAsHint().clone(), - check_ignore: self.check_ignore().clone(), + remap_prefix: self.diagnostics_remapPrefix(source_root).clone(), + warnings_as_info: self.diagnostics_warningsAsInfo(source_root).clone(), + warnings_as_hint: self.diagnostics_warningsAsHint(source_root).clone(), + check_ignore: self.check_ignore(source_root).clone(), } } - pub fn extra_args(&self) -> &Vec { - self.cargo_extraArgs() + pub fn extra_args(&self, source_root: Option) -> &Vec { + self.cargo_extraArgs(source_root) } - pub fn extra_env(&self) -> &FxHashMap { - self.cargo_extraEnv() + pub fn extra_env(&self, source_root: Option) -> &FxHashMap { + self.cargo_extraEnv(source_root) } - pub fn check_extra_args(&self) -> Vec { - let mut extra_args = self.extra_args().clone(); - extra_args.extend_from_slice(self.check_extraArgs()); + pub fn check_extra_args(&self, source_root: Option) -> Vec { + let mut extra_args = self.extra_args(source_root).clone(); + extra_args.extend_from_slice(self.check_extraArgs(source_root)); extra_args } - pub fn check_extra_env(&self) -> FxHashMap { - let mut extra_env = self.cargo_extraEnv().clone(); - extra_env.extend(self.check_extraEnv().clone()); + pub fn check_extra_env(&self, source_root: Option) -> FxHashMap { + let mut extra_env = self.cargo_extraEnv(source_root).clone(); + extra_env.extend(self.check_extraEnv(source_root).clone()); extra_env } @@ -1761,8 +1765,11 @@ impl Config { Some(AbsPathBuf::try_from(path).unwrap_or_else(|path| self.root_path.join(path))) } - pub fn ignored_proc_macros(&self) -> &FxHashMap, Box<[Box]>> { - self.procMacro_ignored() + pub fn ignored_proc_macros( + &self, + source_root: Option, + ) -> &FxHashMap, Box<[Box]>> { + self.procMacro_ignored(source_root) } pub fn expand_proc_macros(&self) -> bool { @@ -1787,23 +1794,23 @@ impl Config { } } - pub fn cargo_autoreload_config(&self) -> bool { - self.cargo_autoreload().to_owned() + pub fn cargo_autoreload_config(&self, source_root: Option) -> bool { + self.cargo_autoreload(source_root).to_owned() } - pub fn run_build_scripts(&self) -> bool { - self.cargo_buildScripts_enable().to_owned() || self.procMacro_enable().to_owned() + pub fn run_build_scripts(&self, source_root: Option) -> bool { + self.cargo_buildScripts_enable(source_root).to_owned() || self.procMacro_enable().to_owned() } - pub fn cargo(&self) -> CargoConfig { - let rustc_source = self.rustc_source().as_ref().map(|rustc_src| { + pub fn cargo(&self, source_root: Option) -> CargoConfig { + let rustc_source = self.rustc_source(source_root).as_ref().map(|rustc_src| { if rustc_src == "discover" { RustLibSource::Discover } else { RustLibSource::Path(self.root_path.join(rustc_src)) } }); - let sysroot = self.cargo_sysroot().as_ref().map(|sysroot| { + let sysroot = self.cargo_sysroot(source_root).as_ref().map(|sysroot| { if sysroot == "discover" { RustLibSource::Discover } else { @@ -1811,24 +1818,24 @@ impl Config { } }); let sysroot_src = - self.cargo_sysrootSrc().as_ref().map(|sysroot| self.root_path.join(sysroot)); + self.cargo_sysrootSrc(source_root).as_ref().map(|sysroot| self.root_path.join(sysroot)); CargoConfig { - all_targets: *self.cargo_allTargets(), - features: match &self.cargo_features() { + all_targets: *self.cargo_allTargets(source_root), + features: match &self.cargo_features(source_root) { CargoFeaturesDef::All => CargoFeatures::All, CargoFeaturesDef::Selected(features) => CargoFeatures::Selected { features: features.clone(), - no_default_features: self.cargo_noDefaultFeatures().to_owned(), + no_default_features: self.cargo_noDefaultFeatures(source_root).to_owned(), }, }, - target: self.cargo_target().clone(), + target: self.cargo_target(source_root).clone(), sysroot, sysroot_src, rustc_source, cfg_overrides: project_model::CfgOverrides { global: CfgDiff::new( - self.cargo_cfgs() + self.cargo_cfgs(source_root) .iter() .map(|(key, val)| match val { Some(val) => CfgAtom::KeyValue { @@ -1843,18 +1850,65 @@ impl Config { .unwrap(), selective: Default::default(), }, - wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(), - invocation_strategy: match self.cargo_buildScripts_invocationStrategy() { + wrap_rustc_in_build_scripts: *self.cargo_buildScripts_useRustcWrapper(source_root), + invocation_strategy: match self.cargo_buildScripts_invocationStrategy(source_root) { InvocationStrategy::Once => project_model::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => project_model::InvocationStrategy::PerWorkspace, }, - run_build_script_command: self.cargo_buildScripts_overrideCommand().clone(), - extra_args: self.cargo_extraArgs().clone(), - extra_env: self.cargo_extraEnv().clone(), - target_dir: self.target_dir_from_config(), + run_build_script_command: self.cargo_buildScripts_overrideCommand(source_root).clone(), + extra_args: self.cargo_extraArgs(source_root).clone(), + extra_env: self.cargo_extraEnv(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } + pub(crate) fn completion_snippets_default() -> FxHashMap { + serde_json::from_str( + r#"{ + "Arc::new": { + "postfix": "arc", + "body": "Arc::new(${receiver})", + "requires": "std::sync::Arc", + "description": "Put the expression into an `Arc`", + "scope": "expr" + }, + "Rc::new": { + "postfix": "rc", + "body": "Rc::new(${receiver})", + "requires": "std::rc::Rc", + "description": "Put the expression into an `Rc`", + "scope": "expr" + }, + "Box::pin": { + "postfix": "pinbox", + "body": "Box::pin(${receiver})", + "requires": "std::boxed::Box", + "description": "Put the expression into a pinned `Box`", + "scope": "expr" + }, + "Ok": { + "postfix": "ok", + "body": "Ok(${receiver})", + "description": "Wrap the expression in a `Result::Ok`", + "scope": "expr" + }, + "Err": { + "postfix": "err", + "body": "Err(${receiver})", + "description": "Wrap the expression in a `Result::Err`", + "scope": "expr" + }, + "Some": { + "postfix": "some", + "body": "Some(${receiver})", + "description": "Wrap the expression in an `Option::Some`", + "scope": "expr" + } + }"#, + ) + .unwrap() + } + pub fn rustfmt(&self, source_root_id: Option) -> RustfmtConfig { match &self.rustfmt_overrideCommand(source_root_id) { Some(args) if !args.is_empty() => { @@ -1869,37 +1923,37 @@ impl Config { } } - pub fn flycheck_workspace(&self) -> bool { - *self.check_workspace() + pub fn flycheck_workspace(&self, source_root: Option) -> bool { + *self.check_workspace(source_root) } - pub(crate) fn cargo_test_options(&self) -> CargoOptions { + pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { - target_triples: self.cargo_target().clone().into_iter().collect(), + target_triples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, - no_default_features: *self.cargo_noDefaultFeatures(), - all_features: matches!(self.cargo_features(), CargoFeaturesDef::All), - features: match self.cargo_features().clone() { + no_default_features: *self.cargo_noDefaultFeatures(source_root), + all_features: matches!(self.cargo_features(source_root), CargoFeaturesDef::All), + features: match self.cargo_features(source_root).clone() { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.extra_args().clone(), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.extra_env().clone(), - target_dir: self.target_dir_from_config(), + extra_args: self.extra_args(source_root).clone(), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), + extra_env: self.extra_env(source_root).clone(), + target_dir: self.target_dir_from_config(source_root), } } - pub(crate) fn flycheck(&self) -> FlycheckConfig { - match &self.check_overrideCommand() { + pub(crate) fn flycheck(&self, source_root: Option) -> FlycheckConfig { + match &self.check_overrideCommand(source_root) { Some(args) if !args.is_empty() => { let mut args = args.clone(); let command = args.remove(0); FlycheckConfig::CustomCommand { command, args, - extra_env: self.check_extra_env(), - invocation_strategy: match self.check_invocationStrategy() { + extra_env: self.check_extra_env(source_root), + invocation_strategy: match self.check_invocationStrategy(source_root) { InvocationStrategy::Once => crate::flycheck::InvocationStrategy::Once, InvocationStrategy::PerWorkspace => { crate::flycheck::InvocationStrategy::PerWorkspace @@ -1908,44 +1962,50 @@ impl Config { } } Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command().clone(), + command: self.check_command(source_root).clone(), options: CargoOptions { target_triples: self - .check_targets() + .check_targets(source_root) .clone() .and_then(|targets| match &targets.0[..] { [] => None, targets => Some(targets.into()), }) - .unwrap_or_else(|| self.cargo_target().clone().into_iter().collect()), - all_targets: self.check_allTargets().unwrap_or(*self.cargo_allTargets()), + .unwrap_or_else(|| { + self.cargo_target(source_root).clone().into_iter().collect() + }), + all_targets: self + .check_allTargets(source_root) + .unwrap_or(*self.cargo_allTargets(source_root)), no_default_features: self - .check_noDefaultFeatures() - .unwrap_or(*self.cargo_noDefaultFeatures()), + .check_noDefaultFeatures(source_root) + .unwrap_or(*self.cargo_noDefaultFeatures(source_root)), all_features: matches!( - self.check_features().as_ref().unwrap_or(self.cargo_features()), + self.check_features(source_root) + .as_ref() + .unwrap_or(self.cargo_features(source_root)), CargoFeaturesDef::All ), features: match self - .check_features() + .check_features(source_root) .clone() - .unwrap_or_else(|| self.cargo_features().clone()) + .unwrap_or_else(|| self.cargo_features(source_root).clone()) { CargoFeaturesDef::All => vec![], CargoFeaturesDef::Selected(it) => it, }, - extra_args: self.check_extra_args(), - extra_test_bin_args: self.runnables_extraTestBinaryArgs().clone(), - extra_env: self.check_extra_env(), - target_dir: self.target_dir_from_config(), + extra_args: self.check_extra_args(source_root), + extra_test_bin_args: self.runnables_extraTestBinaryArgs(source_root).clone(), + extra_env: self.check_extra_env(source_root), + target_dir: self.target_dir_from_config(source_root), }, ansi_color_output: self.color_diagnostic_output(), }, } } - fn target_dir_from_config(&self) -> Option { - self.cargo_targetDir().as_ref().and_then(|target_dir| match target_dir { + fn target_dir_from_config(&self, source_root: Option) -> Option { + self.cargo_targetDir(source_root).as_ref().and_then(|target_dir| match target_dir { TargetDirectory::UseSubdirectory(true) => { Some(Utf8PathBuf::from("target/rust-analyzer")) } @@ -1955,19 +2015,19 @@ impl Config { }) } - pub fn check_on_save(&self) -> bool { - *self.checkOnSave() + pub fn check_on_save(&self, source_root: Option) -> bool { + *self.checkOnSave(source_root) } - pub fn script_rebuild_on_save(&self) -> bool { - *self.cargo_buildScripts_rebuildOnSave() + pub fn script_rebuild_on_save(&self, source_root: Option) -> bool { + *self.cargo_buildScripts_rebuildOnSave(source_root) } - pub fn runnables(&self) -> RunnablesConfig { + pub fn runnables(&self, source_root: Option) -> RunnablesConfig { RunnablesConfig { - override_cargo: self.runnables_command().clone(), - cargo_extra_args: self.runnables_extraArgs().clone(), - extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(), + override_cargo: self.runnables_command(source_root).clone(), + cargo_extra_args: self.runnables_extraArgs(source_root).clone(), + extra_test_binary_args: self.runnables_extraTestBinaryArgs(source_root).clone(), } } @@ -2006,19 +2066,19 @@ impl Config { } } - pub fn workspace_symbol(&self) -> WorkspaceSymbolConfig { + pub fn workspace_symbol(&self, source_root: Option) -> WorkspaceSymbolConfig { WorkspaceSymbolConfig { - search_scope: match self.workspace_symbol_search_scope() { + search_scope: match self.workspace_symbol_search_scope(source_root) { WorkspaceSymbolSearchScopeDef::Workspace => WorkspaceSymbolSearchScope::Workspace, WorkspaceSymbolSearchScopeDef::WorkspaceAndDependencies => { WorkspaceSymbolSearchScope::WorkspaceAndDependencies } }, - search_kind: match self.workspace_symbol_search_kind() { + search_kind: match self.workspace_symbol_search_kind(source_root) { WorkspaceSymbolSearchKindDef::OnlyTypes => WorkspaceSymbolSearchKind::OnlyTypes, WorkspaceSymbolSearchKindDef::AllSymbols => WorkspaceSymbolSearchKind::AllSymbols, }, - search_limit: *self.workspace_symbol_search_limit(), + search_limit: *self.workspace_symbol_search_limit(source_root), } } @@ -2259,18 +2319,6 @@ where se.serialize_str(path.as_str()) } -impl ManifestOrProjectJson { - fn manifest(&self) -> Option<&Utf8Path> { - match self { - ManifestOrProjectJson::Manifest(manifest) => Some(manifest), - ManifestOrProjectJson::DiscoveredProjectJson { buildfile, .. } => { - Some(buildfile.as_ref()) - } - ManifestOrProjectJson::ProjectJson(_) => None, - } - } -} - #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ExprFillDefaultDef { @@ -3522,9 +3570,9 @@ mod tests { })); (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(), &None); + assert_eq!(config.cargo_targetDir(None), &None); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir.is_none()) ); } @@ -3540,9 +3588,9 @@ mod tests { (config, _, _) = config.apply_change(change); - assert_eq!(config.cargo_targetDir(), &Some(TargetDirectory::UseSubdirectory(true))); + assert_eq!(config.cargo_targetDir(None), &Some(TargetDirectory::UseSubdirectory(true))); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("target/rust-analyzer"))) ); } @@ -3559,83 +3607,11 @@ mod tests { (config, _, _) = config.apply_change(change); assert_eq!( - config.cargo_targetDir(), + config.cargo_targetDir(None), &Some(TargetDirectory::Directory(Utf8PathBuf::from("other_folder"))) ); assert!( - matches!(config.flycheck(), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) + matches!(config.flycheck(None), FlycheckConfig::CargoCommand { options, .. } if options.target_dir == Some(Utf8PathBuf::from("other_folder"))) ); } - - #[test] - fn toml_unknown_key() { - let config = - Config::new(AbsPathBuf::assert(project_root()), Default::default(), vec![], None); - - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - - [invalid.config] - err = "error" - - [cargo] - target = "ok" - - // FIXME: This should be an error - [cargo.sysroot] - non-table = "expected" - } - .to_string() - .into(), - )); - - let (config, e, _) = config.apply_change(change); - expect_test::expect![[r#" - ConfigErrors( - [ - Toml { - config_key: "invalid/config/err", - error: Error { - inner: Error { - inner: TomlError { - message: "unexpected field", - raw: None, - keys: [], - span: None, - }, - }, - }, - }, - ], - ) - "#]] - .assert_debug_eq(&e); - let mut change = ConfigChange::default(); - - change.change_user_config(Some( - toml::toml! { - [cargo.cfgs] - these = "these" - should = "should" - be = "be" - valid = "valid" - } - .to_string() - .into(), - )); - let (_, e, _) = config.apply_change(change); - expect_test::expect![[r#" - ConfigErrors( - [], - ) - "#]] - .assert_debug_eq(&e); - } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs index 9d0082c370c7..89487aa673bf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs @@ -540,7 +540,7 @@ impl GlobalState { } pub(crate) fn respond(&mut self, response: lsp_server::Response) { - if let Some((method, start)) = self.req_queue.incoming.complete(response.id.clone()) { + if let Some((method, start)) = self.req_queue.incoming.complete(&response.id) { if let Some(err) = &response.error { if err.message.starts_with("server panicked") { self.poke_rust_analyzer_developer(format!("{}, check the log", err.message)) @@ -631,6 +631,10 @@ impl GlobalStateSnapshot { file_id_to_url(&self.vfs_read(), id) } + pub(crate) fn vfs_path_to_file_id(&self, vfs_path: &VfsPath) -> anyhow::Result { + vfs_path_to_file_id(&self.vfs_read(), vfs_path) + } + pub(crate) fn file_line_index(&self, file_id: FileId) -> Cancellable { let endings = self.vfs.read().1[&file_id]; let index = self.analysis.file_line_index(file_id)?; @@ -725,3 +729,9 @@ pub(crate) fn url_to_file_id(vfs: &vfs::Vfs, url: &Url) -> anyhow::Result anyhow::Result { + let res = + vfs.file_id(vfs_path).ok_or_else(|| anyhow::format_err!("file not found: {vfs_path}"))?; + Ok(res) +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs index f03de8ce0f03..ed7bf27843b5 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs @@ -325,14 +325,14 @@ impl NotificationDispatcher<'_> { pub(crate) fn on_sync_mut( &mut self, f: fn(&mut GlobalState, N::Params) -> anyhow::Result<()>, - ) -> anyhow::Result<&mut Self> + ) -> &mut Self where N: lsp_types::notification::Notification, N::Params: DeserializeOwned + Send + Debug, { let not = match self.not.take() { Some(it) => it, - None => return Ok(self), + None => return self, }; let _guard = tracing::info_span!("notification", method = ?not.method).entered(); @@ -344,7 +344,7 @@ impl NotificationDispatcher<'_> { } Err(ExtractError::MethodMismatch(not)) => { self.not = Some(not); - return Ok(self); + return self; } }; @@ -355,8 +355,10 @@ impl NotificationDispatcher<'_> { version(), N::METHOD )); - f(self.global_state, params)?; - Ok(self) + if let Err(e) = f(self.global_state, params) { + tracing::error!(handler = %N::METHOD, error = %e, "notification handler failed"); + } + self } pub(crate) fn finish(&mut self) { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 38b88ff2d040..49b1ba32a795 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -145,14 +145,18 @@ pub(crate) fn handle_did_save_text_document( state: &mut GlobalState, params: DidSaveTextDocumentParams, ) -> anyhow::Result<()> { - if state.config.script_rebuild_on_save() && state.build_deps_changed { - state.build_deps_changed = false; - state - .fetch_build_data_queue - .request_op("build_deps_changed - save notification".to_owned(), ()); - } - if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + let snap = state.snapshot(); + let file_id = snap.vfs_path_to_file_id(&vfs_path)?; + let sr = snap.analysis.source_root_id(file_id)?; + + if state.config.script_rebuild_on_save(Some(sr)) && state.build_deps_changed { + state.build_deps_changed = false; + state + .fetch_build_data_queue + .request_op("build_deps_changed - save notification".to_owned(), ()); + } + // Re-fetch workspaces if a workspace related file has changed if let Some(path) = vfs_path.as_path() { let additional_files = &state @@ -182,15 +186,16 @@ pub(crate) fn handle_did_save_text_document( } } - if !state.config.check_on_save() || run_flycheck(state, vfs_path) { + if !state.config.check_on_save(Some(sr)) || run_flycheck(state, vfs_path) { return Ok(()); } - } else if state.config.check_on_save() { + } else if state.config.check_on_save(None) { // No specific flycheck was triggered, so let's trigger all of them. for flycheck in state.flycheck.iter() { flycheck.restart_workspace(None); } } + Ok(()) } @@ -288,6 +293,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { let file_id = state.vfs.read().0.file_id(&vfs_path); if let Some(file_id) = file_id { let world = state.snapshot(); + let source_root_id = world.analysis.source_root_id(file_id).ok(); let mut updated = false; let task = move || -> std::result::Result<(), ide::Cancelled> { // Is the target binary? If so we let flycheck run only for the workspace that contains the crate. @@ -351,7 +357,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { .targets .iter() .any(|&it| crate_root_paths.contains(&cargo[it].root.as_path())); - has_target_with_root.then(|| cargo[pkg].name.clone()) + has_target_with_root.then(|| cargo.package_flag(&cargo[pkg])) }), project_model::ProjectWorkspaceKind::Json(project) => { if !project.crates().any(|(_, krate)| { @@ -373,9 +379,9 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { for (id, package) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - match package - .filter(|_| !world.config.flycheck_workspace() || target.is_some()) - { + match package.filter(|_| { + !world.config.flycheck_workspace(source_root_id) || target.is_some() + }) { Some(package) => flycheck .restart_for_package(package, target.clone().map(TupleExt::head)), None => flycheck.restart_workspace(saved_file.clone()), diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs index 1ad5ff0c8cdc..bcbd970a0d21 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs @@ -40,7 +40,10 @@ use crate::{ hack_recover_crate_name, line_index::LineEndings, lsp::{ - ext::InternalTestingFetchConfigParams, + ext::{ + InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, + }, from_proto, to_proto, utils::{all_edits_are_disjoint, invalid_params_error}, LspError, @@ -256,7 +259,7 @@ pub(crate) fn handle_run_test( let handle = CargoTestHandle::new( test_path, - state.config.cargo_test_options(), + state.config.cargo_test_options(None), cargo.workspace_root(), test_target, state.test_run_sender.clone(), @@ -565,7 +568,7 @@ pub(crate) fn handle_workspace_symbol( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_workspace_symbol").entered(); - let config = snap.config.workspace_symbol(); + let config = snap.config.workspace_symbol(None); let (all_symbols, libs) = decide_search_scope_and_kind(¶ms, &config); let query = { @@ -852,6 +855,7 @@ pub(crate) fn handle_runnables( ) -> anyhow::Result> { let _p = tracing::info_span!("handle_runnables").entered(); let file_id = from_proto::file_id(&snap, ¶ms.text_document.uri)?; + let source_root = snap.analysis.source_root_id(file_id).ok(); let line_index = snap.file_line_index(file_id)?; let offset = params.position.and_then(|it| from_proto::offset(&line_index, it).ok()); let target_spec = TargetSpec::for_file(&snap, file_id)?; @@ -894,7 +898,7 @@ pub(crate) fn handle_runnables( } // Add `cargo check` and `cargo test` for all targets of the whole package - let config = snap.config.runnables(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { let is_crate_no_std = snap.analysis.is_crate_no_std(spec.crate_id)?; @@ -1602,14 +1606,14 @@ pub(crate) fn handle_inlay_hints_resolve( anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data"); let line_index = snap.file_line_index(file_id)?; - let hint_position = from_proto::offset(&line_index, original_hint.position)?; + let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?; let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints(); forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty(); let resolve_hints = snap.analysis.inlay_hints_resolve( &forced_resolve_inlay_hints_config, file_id, - hint_position, + range, hash, |hint| { std::hash::BuildHasher::hash_one( @@ -2119,7 +2123,7 @@ fn run_rustfmt( RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { // FIXME: Set RUSTUP_TOOLCHAIN let mut cmd = process::Command::new(toolchain::Tool::Rustfmt.path()); - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(extra_args); if let Some(edition) = edition { @@ -2177,7 +2181,7 @@ fn run_rustfmt( _ => process::Command::new(cmd), }; - cmd.envs(snap.config.extra_env()); + cmd.envs(snap.config.extra_env(source_root_id)); cmd.args(args); cmd } @@ -2291,7 +2295,7 @@ pub(crate) fn fetch_dependency_list( pub(crate) fn internal_testing_fetch_config( state: GlobalStateSnapshot, params: InternalTestingFetchConfigParams, -) -> anyhow::Result { +) -> anyhow::Result> { let source_root = params .text_document .map(|it| { @@ -2301,15 +2305,18 @@ pub(crate) fn internal_testing_fetch_config( .map_err(anyhow::Error::from) }) .transpose()?; - serde_json::to_value(match &*params.config { - "local" => state.config.assist(source_root).assist_emit_must_use, - "workspace" => matches!( - state.config.rustfmt(source_root), - RustfmtConfig::Rustfmt { enable_range_formatting: true, .. } - ), - _ => return Err(anyhow::anyhow!("Unknown test config key: {}", params.config)), - }) - .map_err(Into::into) + Ok(Some(match params.config { + InternalTestingFetchConfigOption::AssistEmitMustUse => { + InternalTestingFetchConfigResponse::AssistEmitMustUse( + state.config.assist(source_root).assist_emit_must_use, + ) + } + InternalTestingFetchConfigOption::CheckWorkspace => { + InternalTestingFetchConfigResponse::CheckWorkspace( + state.config.flycheck_workspace(source_root), + ) + } + })) } /// Searches for the directory of a Rust crate given this crate's root file path. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs index 28f4b809d6c8..118469df730c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -167,6 +167,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -213,6 +214,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; @@ -257,6 +259,7 @@ fn integrated_completion_benchmark() { prefer_absolute: false, snippets: Vec::new(), limit: None, + add_semicolon_to_unit: true, }; let position = FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() }; diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs index 8d1a686dc4d4..618481bbc66b 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs @@ -16,9 +16,22 @@ use serde::{Deserialize, Serialize}; pub enum InternalTestingFetchConfig {} +#[derive(Deserialize, Serialize, Debug)] +pub enum InternalTestingFetchConfigOption { + AssistEmitMustUse, + CheckWorkspace, +} + +#[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] +pub enum InternalTestingFetchConfigResponse { + AssistEmitMustUse(bool), + CheckWorkspace(bool), +} + impl Request for InternalTestingFetchConfig { type Params = InternalTestingFetchConfigParams; - type Result = serde_json::Value; + // Option is solely to circumvent Default bound. + type Result = Option; const METHOD: &'static str = "rust-analyzer-internal/internalTestingFetchConfig"; } @@ -26,7 +39,7 @@ impl Request for InternalTestingFetchConfig { #[serde(rename_all = "camelCase")] pub struct InternalTestingFetchConfigParams { pub text_document: Option, - pub config: String, + pub config: InternalTestingFetchConfigOption, } pub enum AnalyzerStatus {} @@ -819,6 +832,7 @@ pub struct InlayHintResolveData { pub file_id: u32, // This is a string instead of a u64 as javascript can't represent u64 fully pub hash: String, + pub resolve_range: lsp_types::Range, pub version: Option, } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs index eb6bc2a9ce9b..4902c9f88c14 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs @@ -80,6 +80,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { | SymbolKind::ValueParam | SymbolKind::Label => lsp_types::SymbolKind::VARIABLE, SymbolKind::Union => lsp_types::SymbolKind::STRUCT, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::SymbolKind::VARIABLE, } } @@ -159,6 +160,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Variant => lsp_types::CompletionItemKind::ENUM_MEMBER, SymbolKind::BuiltinAttr => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::ToolModule => lsp_types::CompletionItemKind::MODULE, + SymbolKind::InlineAsmRegOrRegClass => lsp_types::CompletionItemKind::KEYWORD, }, } } @@ -228,8 +230,12 @@ pub(crate) fn completion_items( line_index: &LineIndex, version: Option, tdpp: lsp_types::TextDocumentPositionParams, - items: Vec, + mut items: Vec, ) -> Vec { + if config.completion_hide_deprecated() { + items.retain(|item| !item.deprecated); + } + let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default(); let mut res = Vec::with_capacity(items.len()); for item in items { @@ -452,10 +458,13 @@ pub(crate) fn inlay_hint( file_id: FileId, mut inlay_hint: InlayHint, ) -> Cancellable { - let resolve_hash = inlay_hint.needs_resolve().then(|| { - std::hash::BuildHasher::hash_one( - &std::hash::BuildHasherDefault::::default(), - &inlay_hint, + let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| { + ( + range, + std::hash::BuildHasher::hash_one( + &std::hash::BuildHasherDefault::::default(), + &inlay_hint, + ), ) }); @@ -465,7 +474,7 @@ pub(crate) fn inlay_hint( .visual_studio_code_version() // https://github.com/microsoft/vscode/issues/193124 .map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version)) - && resolve_hash.is_some() + && resolve_range_and_hash.is_some() && fields_to_resolve.resolve_text_edits { something_to_resolve |= inlay_hint.text_edit.is_some(); @@ -477,16 +486,17 @@ pub(crate) fn inlay_hint( snap, fields_to_resolve, &mut something_to_resolve, - resolve_hash.is_some(), + resolve_range_and_hash.is_some(), inlay_hint.label, )?; - let data = match resolve_hash { - Some(hash) if something_to_resolve => Some( + let data = match resolve_range_and_hash { + Some((resolve_range, hash)) if something_to_resolve => Some( to_value(lsp_ext::InlayHintResolveData { file_id: file_id.index(), hash: hash.to_string(), version: snap.file_version(file_id), + resolve_range: range(line_index, resolve_range), }) .unwrap(), ), @@ -694,6 +704,7 @@ fn semantic_token_type_and_modifiers( SymbolKind::ProcMacro => types::PROC_MACRO, SymbolKind::BuiltinAttr => types::BUILTIN_ATTRIBUTE, SymbolKind::ToolModule => types::TOOL_MODULE, + SymbolKind::InlineAsmRegOrRegClass => types::KEYWORD, }, HlTag::AttributeBracket => types::ATTRIBUTE_BRACKET, HlTag::BoolLiteral => types::BOOLEAN, @@ -1365,8 +1376,9 @@ pub(crate) fn runnable( snap: &GlobalStateSnapshot, runnable: Runnable, ) -> Cancellable> { - let config = snap.config.runnables(); let target_spec = TargetSpec::for_file(snap, runnable.nav.file_id)?; + let source_root = snap.analysis.source_root_id(runnable.nav.file_id).ok(); + let config = snap.config.runnables(source_root); match target_spec { Some(TargetSpec::Cargo(spec)) => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index 616d6b49351c..835592302576 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -4,6 +4,7 @@ use std::{ fmt, ops::Div as _, + panic::AssertUnwindSafe, time::{Duration, Instant}, }; @@ -195,7 +196,7 @@ impl GlobalState { ) { return Ok(()); } - self.handle_event(event)?; + self.handle_event(event); } Err(anyhow::anyhow!("A receiver has been dropped, something panicked!")) @@ -277,7 +278,7 @@ impl GlobalState { .map(Some) } - fn handle_event(&mut self, event: Event) -> anyhow::Result<()> { + fn handle_event(&mut self, event: Event) { let loop_start = Instant::now(); let _p = tracing::info_span!("GlobalState::handle_event", event = %event).entered(); @@ -294,7 +295,7 @@ impl GlobalState { match event { Event::Lsp(msg) => match msg { lsp_server::Message::Request(req) => self.on_new_request(loop_start, req), - lsp_server::Message::Notification(not) => self.on_notification(not)?, + lsp_server::Message::Notification(not) => self.on_notification(not), lsp_server::Message::Response(resp) => self.complete_request(resp), }, Event::QueuedTask(task) => { @@ -404,7 +405,7 @@ impl GlobalState { if self.is_quiescent() { let became_quiescent = !was_quiescent; if became_quiescent { - if self.config.check_on_save() { + if self.config.check_on_save(None) { // Project has loaded properly, kick off initial flycheck self.flycheck.iter().for_each(|flycheck| flycheck.restart_workspace(None)); } @@ -434,7 +435,7 @@ impl GlobalState { let project_or_mem_docs_changed = became_quiescent || state_changed || memdocs_added_or_removed; - if project_or_mem_docs_changed && self.config.publish_diagnostics() { + if project_or_mem_docs_changed && self.config.publish_diagnostics(None) { self.update_diagnostics(); } if project_or_mem_docs_changed && self.config.test_explorer() { @@ -455,7 +456,7 @@ impl GlobalState { } } - if self.config.cargo_autoreload_config() + if self.config.cargo_autoreload_config(None) || self.config.discover_workspace_config().is_some() { if let Some((cause, FetchWorkspaceRequest { path, force_crate_graph_reload })) = @@ -486,7 +487,6 @@ impl GlobalState { "overly long loop turn took {loop_duration:?} (event handling took {event_handling_duration:?}): {event_dbg_msg}" )); } - Ok(()) } fn prime_caches(&mut self, cause: String) { @@ -552,23 +552,33 @@ impl GlobalState { let fetch_semantic = self.vfs_done && self.fetch_workspaces_queue.last_op_result().is_some(); move |sender| { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions.clone(), - slice.clone(), - NativeDiagnosticsFetchKind::Syntax, - ); + // We aren't observing the semantics token cache here + let snapshot = AssertUnwindSafe(&snapshot); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Syntax, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let diags = fetch_native_diagnostics( - &snapshot, - subscriptions, - slice, - NativeDiagnosticsFetchKind::Semantic, - ); + let Ok(diags) = std::panic::catch_unwind(|| { + fetch_native_diagnostics( + &snapshot, + subscriptions.clone(), + slice.clone(), + NativeDiagnosticsFetchKind::Semantic, + ) + }) else { + return; + }; sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, @@ -875,7 +885,7 @@ impl GlobalState { self.discover_workspace_queue.op_completed(()); let mut config = Config::clone(&*self.config); - config.add_linked_projects(project, buildfile); + config.add_discovered_project_from_command(project, buildfile); self.update_configuration(config); } DiscoverProjectMessage::Progress { message } => { @@ -925,7 +935,7 @@ impl GlobalState { FlycheckMessage::AddDiagnostic { id, workspace_root, diagnostic } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( - &self.config.diagnostics_map(), + &self.config.diagnostics_map(None), &diagnostic, &workspace_root, &snap, @@ -973,9 +983,9 @@ impl GlobalState { // When we're running multiple flychecks, we have to include a disambiguator in // the title, or the editor complains. Note that this is a user-facing string. let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck()) + format!("{}", self.config.flycheck(None)) } else { - format!("{} (#{})", self.config.flycheck(), id + 1) + format!("{} (#{})", self.config.flycheck(None), id + 1) }; self.report_progress( &title, @@ -1105,37 +1115,32 @@ impl GlobalState { } /// Handles an incoming notification. - fn on_notification(&mut self, not: Notification) -> anyhow::Result<()> { + fn on_notification(&mut self, not: Notification) { let _p = span!(Level::INFO, "GlobalState::on_notification", not.method = ?not.method).entered(); use crate::handlers::notification as handlers; use lsp_types::notification as notifs; NotificationDispatcher { not: Some(not), global_state: self } - .on_sync_mut::(handlers::handle_cancel)? + .on_sync_mut::(handlers::handle_cancel) .on_sync_mut::( handlers::handle_work_done_progress_cancel, - )? - .on_sync_mut::(handlers::handle_did_open_text_document)? - .on_sync_mut::( - handlers::handle_did_change_text_document, - )? - .on_sync_mut::(handlers::handle_did_close_text_document)? - .on_sync_mut::(handlers::handle_did_save_text_document)? + ) + .on_sync_mut::(handlers::handle_did_open_text_document) + .on_sync_mut::(handlers::handle_did_change_text_document) + .on_sync_mut::(handlers::handle_did_close_text_document) + .on_sync_mut::(handlers::handle_did_save_text_document) .on_sync_mut::( handlers::handle_did_change_configuration, - )? + ) .on_sync_mut::( handlers::handle_did_change_workspace_folders, - )? - .on_sync_mut::( - handlers::handle_did_change_watched_files, - )? - .on_sync_mut::(handlers::handle_cancel_flycheck)? - .on_sync_mut::(handlers::handle_clear_flycheck)? - .on_sync_mut::(handlers::handle_run_flycheck)? - .on_sync_mut::(handlers::handle_abort_run_test)? + ) + .on_sync_mut::(handlers::handle_did_change_watched_files) + .on_sync_mut::(handlers::handle_cancel_flycheck) + .on_sync_mut::(handlers::handle_clear_flycheck) + .on_sync_mut::(handlers::handle_run_flycheck) + .on_sync_mut::(handlers::handle_abort_run_test) .finish(); - Ok(()) } } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs index eab973387240..5c4c858e1509 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs @@ -37,9 +37,13 @@ impl Default for OpQueue { } impl OpQueue { + /// Request an operation to start. pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { self.op_requested = Some((reason, args)); } + + /// If there was an operation requested, mark this queue as + /// started and return the request arguments. pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> { if self.op_in_progress { return None; @@ -47,18 +51,25 @@ impl OpQueue { self.op_in_progress = self.op_requested.is_some(); self.op_requested.take() } + + /// Mark an operation as completed. pub(crate) fn op_completed(&mut self, result: Output) { assert!(self.op_in_progress); self.op_in_progress = false; self.last_op_result = result; } + /// Get the result of the last operation. pub(crate) fn last_op_result(&self) -> &Output { &self.last_op_result } + + // Is there an operation that has started, but hasn't yet finished? pub(crate) fn op_in_progress(&self) -> bool { self.op_in_progress } + + // Has an operation been requested? pub(crate) fn op_requested(&self) -> bool { self.op_requested.is_some() } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 68366136eda6..f6765715c5a1 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -16,8 +16,9 @@ use std::{iter, mem}; use hir::{db::DefDatabase, ChangeWithProcMacros, ProcMacros, ProcMacrosBuilder}; +use ide::CrateId; use ide_db::{ - base_db::{salsa::Durability, CrateGraph, ProcMacroPaths, Version}, + base_db::{salsa::Durability, CrateGraph, CrateWorkspaceData, ProcMacroPaths}, FxHashMap, }; use itertools::Itertools; @@ -100,7 +101,7 @@ impl GlobalState { { let req = FetchWorkspaceRequest { path: None, force_crate_graph_reload: false }; self.fetch_workspaces_queue.request_op("discovered projects changed".to_owned(), req) - } else if self.config.flycheck() != old_config.flycheck() { + } else if self.config.flycheck(None) != old_config.flycheck(None) { self.reload_flycheck(); } @@ -122,7 +123,7 @@ impl GlobalState { }; let mut message = String::new(); - if !self.config.cargo_autoreload() + if !self.config.cargo_autoreload_config(None) && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() && self.config.discover_workspace_config().is_none() @@ -264,7 +265,7 @@ impl GlobalState { .map(ManifestPath::try_from) .filter_map(Result::ok) .collect(); - let cargo_config = self.config.cargo(); + let cargo_config = self.config.cargo(None); let discover_command = self.config.discover_workspace_config().cloned(); let is_quiescent = !(self.discover_workspace_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version @@ -357,7 +358,7 @@ impl GlobalState { pub(crate) fn fetch_build_data(&mut self, cause: Cause) { info!(%cause, "will fetch build data"); let workspaces = Arc::clone(&self.workspaces); - let config = self.config.cargo(); + let config = self.config.cargo(None); let root_path = self.config.root_path().clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { @@ -382,7 +383,7 @@ impl GlobalState { pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { info!(%cause, "will load proc macros"); - let ignored_proc_macros = self.config.ignored_proc_macros().clone(); + let ignored_proc_macros = self.config.ignored_proc_macros(None).clone(); let proc_macro_clients = self.proc_macro_clients.clone(); self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, move |sender| { @@ -507,7 +508,7 @@ impl GlobalState { // FIXME: can we abort the build scripts here if they are already running? self.workspaces = Arc::new(workspaces); - if self.config.run_build_scripts() { + if self.config.run_build_scripts(None) { self.build_deps_changed = false; self.fetch_build_data_queue.request_op("workspace updated".to_owned(), ()); } @@ -627,7 +628,7 @@ impl GlobalState { .. } => cargo_config_extra_env .iter() - .chain(self.config.extra_env()) + .chain(self.config.extra_env(None)) .map(|(a, b)| (a.clone(), b.clone())) .chain( ws.sysroot @@ -692,7 +693,7 @@ impl GlobalState { }) .collect(); - let (crate_graph, proc_macro_paths, layouts, toolchains) = { + let (crate_graph, proc_macro_paths, ws_data) = { // Create crate graph from all the workspaces let vfs = &mut self.vfs.write().0; @@ -702,7 +703,7 @@ impl GlobalState { vfs.file_id(&vfs_path) }; - ws_to_crate_graph(&self.workspaces, self.config.extra_env(), load) + ws_to_crate_graph(&self.workspaces, self.config.extra_env(None), load) }; let mut change = ChangeWithProcMacros::new(); if self.config.expand_proc_macros() { @@ -721,9 +722,7 @@ impl GlobalState { .collect(), ); } - change.set_crate_graph(crate_graph); - change.set_target_data_layouts(layouts); - change.set_toolchains(toolchains); + change.set_crate_graph(crate_graph, ws_data); self.analysis_host.apply_change(change); self.report_progress( "Building CrateGraph", @@ -791,7 +790,7 @@ impl GlobalState { fn reload_flycheck(&mut self) { let _p = tracing::info_span!("GlobalState::reload_flycheck").entered(); - let config = self.config.flycheck(); + let config = self.config.flycheck(None); let sender = self.flycheck_sender.clone(); let invocation_strategy = match config { FlycheckConfig::CargoCommand { .. } => { @@ -863,51 +862,27 @@ pub fn ws_to_crate_graph( workspaces: &[ProjectWorkspace], extra_env: &FxHashMap, mut load: impl FnMut(&AbsPath) -> Option, -) -> (CrateGraph, Vec, Vec, Arc>>, Vec>) { +) -> (CrateGraph, Vec, FxHashMap>) { let mut crate_graph = CrateGraph::default(); let mut proc_macro_paths = Vec::default(); - let mut layouts = Vec::default(); - let mut toolchains = Vec::default(); - let e = Err(Arc::from("missing layout")); + let mut ws_data = FxHashMap::default(); for ws in workspaces { let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env); - let num_layouts = layouts.len(); - let num_toolchains = toolchains.len(); let ProjectWorkspace { toolchain, target_layout, .. } = ws; - let mapping = crate_graph.extend( - other, - &mut crate_proc_macros, - |(cg_id, cg_data), (_o_id, o_data)| { - // if the newly created crate graph's layout is equal to the crate of the merged graph, then - // we can merge the crates. - let id = cg_id.into_raw().into_u32() as usize; - layouts[id] == *target_layout && toolchains[id] == *toolchain && cg_data == o_data - }, - ); + let mapping = crate_graph.extend(other, &mut crate_proc_macros); // Populate the side tables for the newly merged crates - mapping.values().for_each(|val| { - let idx = val.into_raw().into_u32() as usize; - // we only need to consider crates that were not merged and remapped, as the - // ones that were remapped already have the correct layout and toolchain - if idx >= num_layouts { - if layouts.len() <= idx { - layouts.resize(idx + 1, e.clone()); - } - layouts[idx].clone_from(target_layout); - } - if idx >= num_toolchains { - if toolchains.len() <= idx { - toolchains.resize(idx + 1, None); - } - toolchains[idx].clone_from(toolchain); - } - }); + ws_data.extend(mapping.values().copied().zip(iter::repeat(Arc::new(CrateWorkspaceData { + toolchain: toolchain.clone(), + data_layout: target_layout.clone(), + proc_macro_cwd: Some(ws.workspace_root().to_owned()), + })))); proc_macro_paths.push(crate_proc_macros); } + crate_graph.shrink_to_fit(); proc_macro_paths.shrink_to_fit(); - (crate_graph, proc_macro_paths, layouts, toolchains) + (crate_graph, proc_macro_paths, ws_data) } pub(crate) fn should_refresh_for_change( diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 954e13cbf272..b4aa73d2780d 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -113,7 +113,7 @@ impl CargoTargetSpec { kind: &RunnableKind, cfg: &Option, ) -> (Vec, Vec) { - let config = snap.config.runnables(); + let config = snap.config.runnables(None); let extra_test_binary_args = config.extra_test_binary_args; let mut cargo_args = Vec::new(); @@ -168,7 +168,7 @@ impl CargoTargetSpec { (Default::default(), Default::default()) }; - let cargo_config = snap.config.cargo(); + let cargo_config = snap.config.cargo(None); match &cargo_config.features { CargoFeatures::All => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs deleted file mode 100644 index 04b6713b8d18..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/crate_graph.rs +++ /dev/null @@ -1,126 +0,0 @@ -use std::path::PathBuf; - -use project_model::{ - CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, ProjectWorkspaceKind, Sysroot, - WorkspaceBuildScripts, -}; -use rust_analyzer::ws_to_crate_graph; -use rustc_hash::FxHashMap; -use serde::de::DeserializeOwned; -use vfs::{AbsPathBuf, FileId}; - -fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace { - let meta: Metadata = get_test_json_file(file); - let manifest_path = - ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap(); - let cargo_workspace = CargoWorkspace::new(meta, manifest_path); - ProjectWorkspace { - kind: ProjectWorkspaceKind::Cargo { - cargo: cargo_workspace, - build_scripts: WorkspaceBuildScripts::default(), - rustc: Err(None), - cargo_config_extra_env: Default::default(), - error: None, - }, - sysroot: get_fake_sysroot(), - rustc_cfg: Vec::new(), - cfg_overrides: Default::default(), - toolchain: None, - target_layout: Err("target_data_layout not loaded".into()), - } -} - -fn get_test_json_file(file: &str) -> T { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let file = base.join("tests/test_data").join(file); - let data = std::fs::read_to_string(file).unwrap(); - let mut json = data.parse::().unwrap(); - fixup_paths(&mut json); - return serde_json::from_value(json).unwrap(); - - fn fixup_paths(val: &mut serde_json::Value) { - match val { - serde_json::Value::String(s) => replace_root(s, true), - serde_json::Value::Array(vals) => vals.iter_mut().for_each(fixup_paths), - serde_json::Value::Object(kvals) => kvals.values_mut().for_each(fixup_paths), - serde_json::Value::Null | serde_json::Value::Bool(_) | serde_json::Value::Number(_) => { - } - } - } -} - -fn replace_root(s: &mut String, direction: bool) { - if direction { - let root = if cfg!(windows) { r#"C:\\ROOT\"# } else { "/ROOT/" }; - *s = s.replace("$ROOT$", root) - } else { - let root = if cfg!(windows) { r#"C:\\\\ROOT\\"# } else { "/ROOT/" }; - *s = s.replace(root, "$ROOT$") - } -} - -fn get_fake_sysroot_path() -> PathBuf { - let base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - base.join("../project-model/test_data/fake-sysroot") -} - -fn get_fake_sysroot() -> Sysroot { - let sysroot_path = get_fake_sysroot_path(); - // there's no `libexec/` directory with a `proc-macro-srv` binary in that - // fake sysroot, so we give them both the same path: - let sysroot_dir = AbsPathBuf::assert_utf8(sysroot_path); - let sysroot_src_dir = sysroot_dir.clone(); - Sysroot::load(Some(sysroot_dir), Some(sysroot_src_dir)) -} - -#[test] -fn test_deduplicate_origin_dev() { - let path_map = &mut FxHashMap::default(); - let ws = load_cargo_with_fake_sysroot("deduplication_crate_graph_A.json"); - let ws2 = load_cargo_with_fake_sysroot("deduplication_crate_graph_B.json"); - - let (crate_graph, ..) = ws_to_crate_graph(&[ws, ws2], &Default::default(), |path| { - let len = path_map.len(); - Some(*path_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - }); - - let mut crates_named_p2 = vec![]; - for id in crate_graph.iter() { - let krate = &crate_graph[id]; - if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p2" { - crates_named_p2.push(krate); - } - } - } - - assert_eq!(crates_named_p2.len(), 1); - let p2 = crates_named_p2[0]; - assert!(p2.origin.is_local()); -} - -#[test] -fn test_deduplicate_origin_dev_rev() { - let path_map = &mut FxHashMap::default(); - let ws = load_cargo_with_fake_sysroot("deduplication_crate_graph_B.json"); - let ws2 = load_cargo_with_fake_sysroot("deduplication_crate_graph_A.json"); - - let (crate_graph, ..) = ws_to_crate_graph(&[ws, ws2], &Default::default(), |path| { - let len = path_map.len(); - Some(*path_map.entry(path.to_path_buf()).or_insert(FileId::from_raw(len as u32))) - }); - - let mut crates_named_p2 = vec![]; - for id in crate_graph.iter() { - let krate = &crate_graph[id]; - if let Some(name) = krate.display_name.as_ref() { - if name.to_string() == "p2" { - crates_named_p2.push(krate); - } - } - } - - assert_eq!(crates_named_p2.len(), 1); - let p2 = crates_named_p2[0]; - assert!(p2.origin.is_local()); -} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs index 295d1d4e8e90..a857e0c2967c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/ratoml.rs @@ -9,18 +9,13 @@ use lsp_types::{ use paths::Utf8PathBuf; use rust_analyzer::config::Config; -use rust_analyzer::lsp::ext::{InternalTestingFetchConfig, InternalTestingFetchConfigParams}; +use rust_analyzer::lsp::ext::{ + InternalTestingFetchConfig, InternalTestingFetchConfigOption, InternalTestingFetchConfigParams, + InternalTestingFetchConfigResponse, +}; use serde_json::json; use test_utils::skip_slow_tests; -enum QueryType { - Local, - /// A query whose config key is a part of the global configs, so that - /// testing for changes to this config means testing if global changes - /// take affect. - Workspace, -} - struct RatomlTest { urls: Vec, server: Server, @@ -158,20 +153,24 @@ impl RatomlTest { }); } - fn query(&self, query: QueryType, source_file_idx: usize) -> bool { - let config = match query { - QueryType::Local => "local".to_owned(), - QueryType::Workspace => "workspace".to_owned(), - }; + fn query( + &self, + query: InternalTestingFetchConfigOption, + source_file_idx: usize, + expected: InternalTestingFetchConfigResponse, + ) { let res = self.server.send_request::( InternalTestingFetchConfigParams { text_document: Some(TextDocumentIdentifier { uri: self.urls[source_file_idx].clone(), }), - config, + config: query, }, ); - res.as_bool().unwrap() + assert_eq!( + serde_json::from_value::(res).unwrap(), + expected + ) } } @@ -206,7 +205,11 @@ enum Value { })), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// Checks if client config can be modified. @@ -311,7 +314,11 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 2)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 2, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -341,12 +348,20 @@ enum Value { None, ); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.create( "//- /$$CONFIG_DIR$$/rust-analyzer/rust-analyzer.toml", RatomlTest::EMIT_MUST_USE.to_owned(), ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -378,9 +393,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.edit(2, String::new()); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -412,9 +435,17 @@ assist.emitMustUse = true"#, None, ); - assert!(server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(2); - assert!(!server.query(QueryType::Local, 1)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 1, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -461,7 +492,11 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -508,9 +543,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); server.edit(1, "assist.emitMustUse = true".to_owned()); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -557,9 +600,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -606,9 +657,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.create("//- /p1/p2/rust-analyzer.toml", RatomlTest::EMIT_MUST_NOT_USE.to_owned()); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -656,9 +715,17 @@ pub fn add(left: usize, right: usize) -> usize { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); server.delete(1); - assert!(!server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(false), + ); } #[test] @@ -705,8 +772,16 @@ enum Value { None, ); - assert!(server.query(QueryType::Local, 3)); - assert!(server.query(QueryType::Local, 4)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 4, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } #[test] @@ -744,7 +819,11 @@ fn ratoml_multiple_ratoml_in_single_source_root() { None, ); - assert!(server.query(QueryType::Local, 3)); + server.query( + InternalTestingFetchConfigOption::AssistEmitMustUse, + 3, + InternalTestingFetchConfigResponse::AssistEmitMustUse(true), + ); } /// If a root is non-local, so we cannot find what its parent is @@ -765,7 +844,7 @@ fn ratoml_multiple_ratoml_in_single_source_root() { // [dependencies] // p2 = { path = "../p2" } -// #, +// "#, // r#" // //- /p1/src/lib.rs // enum Value { @@ -836,7 +915,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -848,7 +927,11 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ) } #[test] @@ -868,7 +951,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -880,9 +963,17 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); - server.edit(1, "rustfmt.rangeFormatting.enable = false".to_owned()); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); + server.edit(1, "check.workspace = true".to_owned()); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } #[test] @@ -902,7 +993,7 @@ edition = "2021" "#, r#" //- /p1/rust-analyzer.toml -rustfmt.rangeFormatting.enable = true +check.workspace = false "#, r#" //- /p1/src/lib.rs @@ -914,7 +1005,15 @@ fn main() { None, ); - assert!(server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(false), + ); server.delete(1); - assert!(!server.query(QueryType::Workspace, 2)); + server.query( + InternalTestingFetchConfigOption::CheckWorkspace, + 2, + InternalTestingFetchConfigResponse::CheckWorkspace(true), + ); } diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json b/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json deleted file mode 100644 index b0fb5845cef7..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json +++ /dev/null @@ -1,140 +0,0 @@ -{ - "packages": [ - { - "name": "p1", - "version": "0.1.0", - "id": "p1 0.1.0 (path+file:///example_project/p1)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [ - { - "name": "p2", - "source": null, - "req": "*", - "kind": null, - "rename": null, - "optional": false, - "uses_default_features": true, - "features": [], - "target": null, - "registry": null, - "path": "$ROOT$example_project/p2" - } - ], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p1", - "src_path": "$ROOT$example_project/p1/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p1/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - }, - { - "name": "p2", - "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p2", - "src_path": "$ROOT$example_project/p2/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "p1 0.1.0 (path+file:///example_project/p1)" - ], - "workspace_default_members": [ - "p1 0.1.0 (path+file:///example_project/p1)" - ], - "resolve": { - "nodes": [ - { - "id": "p1 0.1.0 (path+file:///example_project/p1)", - "dependencies": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "deps": [ - { - "name": "p2", - "pkg": "p2 0.1.0 (path+file:///example_project/p2)", - "dep_kinds": [ - { - "kind": null, - "target": null - } - ] - } - ], - "features": [] - }, - { - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "p1 0.1.0 (path+file:///example_project/p1)" - }, - "target_directory": "$ROOT$example_project/p1/target", - "version": 1, - "workspace_root": "$ROOT$example_project/p1", - "metadata": null -} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json b/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json deleted file mode 100644 index b5d1e16e62e0..000000000000 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "packages": [ - { - "name": "p2", - "version": "0.1.0", - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "license": null, - "license_file": null, - "description": null, - "source": null, - "dependencies": [], - "targets": [ - { - "kind": [ - "lib" - ], - "crate_types": [ - "lib" - ], - "name": "p2", - "src_path": "$ROOT$example_project/p2/src/lib.rs", - "edition": "2021", - "doc": true, - "doctest": true, - "test": true - } - ], - "features": {}, - "manifest_path": "$ROOT$example_project/p2/Cargo.toml", - "metadata": null, - "publish": null, - "authors": [], - "categories": [], - "keywords": [], - "readme": null, - "repository": null, - "homepage": null, - "documentation": null, - "edition": "2021", - "links": null, - "default_run": null, - "rust_version": null - } - ], - "workspace_members": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "workspace_default_members": [ - "p2 0.1.0 (path+file:///example_project/p2)" - ], - "resolve": { - "nodes": [ - { - "id": "p2 0.1.0 (path+file:///example_project/p2)", - "dependencies": [], - "deps": [], - "features": [] - } - ], - "root": "p2 0.1.0 (path+file:///example_project/p2)" - }, - "target_directory": "$ROOT$example_project/p2/target", - "version": 1, - "workspace_root": "$ROOT$example_project/p2", - "metadata": null -} \ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 874480c59fbf..cb9c092f5fcd 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -81,7 +81,7 @@ pub struct SyntaxContextData { /// Invariant: Only [`SyntaxContextId::ROOT`] has a [`None`] outer expansion. // FIXME: The None case needs to encode the context crate id. We can encode that as the MSB of // MacroCallId is reserved anyways so we can do bit tagging here just fine. - // The bigger issue is that that will cause interning to now create completely separate chains + // The bigger issue is that this will cause interning to now create completely separate chains // per crate. Though that is likely not a problem as `MacroCallId`s are already crate calling dependent. pub outer_expn: Option, pub outer_transparency: Transparency, diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs index c539754979d6..f80de05ec652 100644 --- a/src/tools/rust-analyzer/crates/span/src/map.rs +++ b/src/tools/rust-analyzer/crates/span/src/map.rs @@ -13,6 +13,7 @@ use crate::{ /// Maps absolute text ranges for the corresponding file to the relevant span data. #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct SpanMap { + /// The offset stored here is the *end* of the node. spans: Vec<(TextSize, SpanData)>, /// Index of the matched macro arm on successful expansion for declarative macros. // FIXME: Does it make sense to have this here? @@ -104,6 +105,52 @@ where pub fn iter(&self) -> impl Iterator)> + '_ { self.spans.iter().copied() } + + /// Merges this span map with another span map, where `other` is inserted at (and replaces) `other_range`. + /// + /// The length of the replacement node needs to be `other_size`. + pub fn merge(&mut self, other_range: TextRange, other_size: TextSize, other: &SpanMap) { + // I find the following diagram helpful to illustrate the bounds and why we use `<` or `<=`: + // -------------------------------------------------------------------- + // 1 3 5 6 7 10 11 <-- offsets we store + // 0-1 1-3 3-5 5-6 6-7 7-10 10-11 <-- ranges these offsets refer to + // 3 .. 7 <-- other_range + // 3-5 5-6 6-7 <-- ranges we replace (len = 7-3 = 4) + // ^^^^^^^^^^^ ^^^^^^^^^^ + // remove shift + // 2 3 5 9 <-- offsets we insert + // 0-2 2-3 3-5 5-9 <-- ranges we insert (other_size = 9-0 = 9) + // ------------------------------------ + // 1 3 + // 0-1 1-3 <-- these remain intact + // 5 6 8 12 + // 3-5 5-6 6-8 8-12 <-- we shift these by other_range.start() and insert them + // 15 16 + // 12-15 15-16 <-- we shift these by other_size-other_range.len() = 9-4 = 5 + // ------------------------------------ + // 1 3 5 6 8 12 15 16 <-- final offsets we store + // 0-1 1-3 3-5 5-6 6-8 8-12 12-15 15-16 <-- final ranges + + self.spans.retain_mut(|(offset, _)| { + if other_range.start() < *offset && *offset <= other_range.end() { + false + } else { + if *offset > other_range.end() { + *offset += other_size; + *offset -= other_range.len(); + } + true + } + }); + + self.spans + .extend(other.spans.iter().map(|&(offset, span)| (offset + other_range.start(), span))); + + self.spans.sort_unstable_by_key(|&(offset, _)| offset); + + // Matched arm info is no longer correct once we have multiple macros. + self.matched_arm = None; + } } #[derive(PartialEq, Eq, Hash, Debug)] diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs index 56e43e82ed27..0ccd08867602 100644 --- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs @@ -17,6 +17,7 @@ use tt::{ token_to_literal, }; +pub mod prettify_macro_expansion; mod to_parser_input; pub use to_parser_input::to_parser_input; // FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces diff --git a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs similarity index 85% rename from src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs rename to src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs index dd4a665e8eb4..fc7caaa98865 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/prettify_macro_expansion.rs @@ -7,13 +7,20 @@ use syntax::{ SyntaxNode, SyntaxToken, WalkEvent, T, }; -// FIXME: It would also be cool to share logic here and in the mbe tests, -// which are pretty unreadable at the moment. /// Renders a [`SyntaxNode`] with whitespace inserted between tokens that require them. -pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { +/// +/// This is an internal API that is only exported because `mbe` needs it for tests and cannot depend +/// on `hir-expand`. For any purpose other than tests, you are supposed to use the `prettify_macro_expansion` +/// from `hir-expand` that handles `$crate` for you. +#[deprecated = "use `hir_expand::prettify_macro_expansion()` instead"] +pub fn prettify_macro_expansion( + syn: SyntaxNode, + dollar_crate_replacement: &mut dyn FnMut(&SyntaxToken) -> SyntaxToken, +) -> SyntaxNode { let mut indent = 0; let mut last: Option = None; let mut mods = Vec::new(); + let mut dollar_crate_replacements = Vec::new(); let syn = syn.clone_subtree().clone_for_update(); let before = Position::before; @@ -51,6 +58,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { } _ => continue, }; + if token.kind() == SyntaxKind::IDENT && token.text() == "$crate" { + dollar_crate_replacements.push((token.clone(), dollar_crate_replacement(&token))); + } let tok = &token; let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool { @@ -122,6 +132,9 @@ pub fn insert_ws_into(syn: SyntaxNode) -> SyntaxNode { for (pos, insert) in mods { ted::insert(pos, insert); } + for (old, new) in dollar_crate_replacements { + ted::replace(old, new); + } if let Some(it) = syn.last_token().filter(|it| it.kind() == SyntaxKind::WHITESPACE) { ted::remove(it); diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram index 43375ce6ae05..52ad439e4dec 100644 --- a/src/tools/rust-analyzer/crates/syntax/rust.ungram +++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram @@ -391,8 +391,33 @@ Expr = OffsetOfExpr = Attr* 'builtin' '#' 'offset_of' '(' Type ',' fields:(NameRef ('.' NameRef)* ) ')' +// asm := "asm!(" format_string *("," format_string) *("," operand) [","] ")" +// global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")" +// format_string := STRING_LITERAL / RAW_STRING_LITERAL AsmExpr = - Attr* 'builtin' '#' 'asm' '(' Expr ')' + Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')' + +// operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_" +AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)? +// dir_spec := "in" / "out" / "lateout" / "inout" / "inlateout" +AsmDirSpec = 'in' | 'out' | 'lateout' | 'inout' | 'inlateout' +// reg_spec := / "\"" "\"" +AsmRegSpec = '@string' | NameRef +// reg_operand := [ident "="] dir_spec "(" reg_spec ")" operand_expr +AsmRegOperand = AsmDirSpec '(' AsmRegSpec ')' AsmOperandExpr +// clobber_abi := "clobber_abi(" *("," ) [","] ")" +AsmClobberAbi = 'clobber_abi' '(' ('@string' (',' '@string')* ','?) ')' +// option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" +AsmOption = 'pure' | 'nomem' | 'readonly' | 'preserves_flags' | 'noreturn' | 'nostack' | 'att_syntax' | 'raw' | 'may_unwind' +// options := "options(" option *("," option) [","] ")" +AsmOptions = 'options' '(' AsmOption *(',' AsmOption) ','? ')' +AsmLabel = 'label' BlockExpr +AsmSym = 'sym' Path +AsmConst = 'const' Expr +// operand := reg_operand / clobber_abi / options +AsmOperand = AsmRegOperand | AsmLabel | AsmSym | AsmConst +AsmOperandNamed = (Name '=')? AsmOperand +AsmPiece = AsmOperandNamed | AsmClobberAbi | AsmOptions FormatArgsExpr = Attr* 'builtin' '#' 'format_args' '(' diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs index c9b39e9922c9..c81a19f3bda4 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs @@ -64,6 +64,53 @@ impl ArrayType { pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmClobberAbi { + pub(crate) syntax: SyntaxNode, +} +impl AsmClobberAbi { + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn clobber_abi_token(&self) -> Option { + support::token(&self.syntax, T![clobber_abi]) + } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmConst { + pub(crate) syntax: SyntaxNode, +} +impl AsmConst { + #[inline] + pub fn expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn const_token(&self) -> Option { support::token(&self.syntax, T![const]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmDirSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmDirSpec { + #[inline] + pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } + #[inline] + pub fn inlateout_token(&self) -> Option { + support::token(&self.syntax, T![inlateout]) + } + #[inline] + pub fn inout_token(&self) -> Option { support::token(&self.syntax, T![inout]) } + #[inline] + pub fn lateout_token(&self) -> Option { support::token(&self.syntax, T![lateout]) } + #[inline] + pub fn out_token(&self) -> Option { support::token(&self.syntax, T![out]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AsmExpr { pub(crate) syntax: SyntaxNode, @@ -71,7 +118,9 @@ pub struct AsmExpr { impl ast::HasAttrs for AsmExpr {} impl AsmExpr { #[inline] - pub fn expr(&self) -> Option { support::child(&self.syntax) } + pub fn asm_pieces(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn template(&self) -> AstChildren { support::children(&self.syntax) } #[inline] pub fn pound_token(&self) -> Option { support::token(&self.syntax, T![#]) } #[inline] @@ -79,11 +128,142 @@ impl AsmExpr { #[inline] pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] pub fn asm_token(&self) -> Option { support::token(&self.syntax, T![asm]) } #[inline] pub fn builtin_token(&self) -> Option { support::token(&self.syntax, T![builtin]) } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmLabel { + pub(crate) syntax: SyntaxNode, +} +impl AsmLabel { + #[inline] + pub fn block_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn label_token(&self) -> Option { support::token(&self.syntax, T![label]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandExpr { + pub(crate) syntax: SyntaxNode, +} +impl AsmOperandExpr { + #[inline] + pub fn in_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn out_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn fat_arrow_token(&self) -> Option { support::token(&self.syntax, T![=>]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOperandNamed { + pub(crate) syntax: SyntaxNode, +} +impl ast::HasName for AsmOperandNamed {} +impl AsmOperandNamed { + #[inline] + pub fn asm_operand(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOption { + pub(crate) syntax: SyntaxNode, +} +impl AsmOption { + #[inline] + pub fn att_syntax_token(&self) -> Option { + support::token(&self.syntax, T![att_syntax]) + } + #[inline] + pub fn may_unwind_token(&self) -> Option { + support::token(&self.syntax, T![may_unwind]) + } + #[inline] + pub fn nomem_token(&self) -> Option { support::token(&self.syntax, T![nomem]) } + #[inline] + pub fn noreturn_token(&self) -> Option { + support::token(&self.syntax, T![noreturn]) + } + #[inline] + pub fn nostack_token(&self) -> Option { support::token(&self.syntax, T![nostack]) } + #[inline] + pub fn preserves_flags_token(&self) -> Option { + support::token(&self.syntax, T![preserves_flags]) + } + #[inline] + pub fn pure_token(&self) -> Option { support::token(&self.syntax, T![pure]) } + #[inline] + pub fn raw_token(&self) -> Option { support::token(&self.syntax, T![raw]) } + #[inline] + pub fn readonly_token(&self) -> Option { + support::token(&self.syntax, T![readonly]) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmOptions { + pub(crate) syntax: SyntaxNode, +} +impl AsmOptions { + #[inline] + pub fn asm_option(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_options(&self) -> AstChildren { support::children(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } + #[inline] + pub fn comma_token(&self) -> Option { support::token(&self.syntax, T![,]) } + #[inline] + pub fn options_token(&self) -> Option { support::token(&self.syntax, T![options]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegOperand { + pub(crate) syntax: SyntaxNode, +} +impl AsmRegOperand { + #[inline] + pub fn asm_dir_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_operand_expr(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn asm_reg_spec(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn l_paren_token(&self) -> Option { support::token(&self.syntax, T!['(']) } + #[inline] + pub fn r_paren_token(&self) -> Option { support::token(&self.syntax, T![')']) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmRegSpec { + pub(crate) syntax: SyntaxNode, +} +impl AsmRegSpec { + #[inline] + pub fn name_ref(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn string_token(&self) -> Option { support::token(&self.syntax, T![string]) } +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct AsmSym { + pub(crate) syntax: SyntaxNode, +} +impl AsmSym { + #[inline] + pub fn path(&self) -> Option { support::child(&self.syntax) } + #[inline] + pub fn sym_token(&self) -> Option { support::token(&self.syntax, T![sym]) } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct AssocItemList { pub(crate) syntax: SyntaxNode, @@ -2051,6 +2231,21 @@ impl ast::HasGenericParams for Adt {} impl ast::HasName for Adt {} impl ast::HasVisibility for Adt {} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmOperand { + AsmConst(AsmConst), + AsmLabel(AsmLabel), + AsmRegOperand(AsmRegOperand), + AsmSym(AsmSym), +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum AsmPiece { + AsmClobberAbi(AsmClobberAbi), + AsmOperandNamed(AsmOperandNamed), + AsmOptions(AsmOptions), +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum AssocItem { Const(Const), @@ -2316,6 +2511,48 @@ impl AstNode for ArrayType { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmClobberAbi { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CLOBBER_ABI } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmConst { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_CONST } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmDirSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_DIR_SPEC } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for AsmExpr { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_EXPR } @@ -2330,6 +2567,118 @@ impl AstNode for AsmExpr { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl AstNode for AsmLabel { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_LABEL } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOperandExpr { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_EXPR } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOperandNamed { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPERAND_NAMED } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOption { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTION } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmOptions { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_OPTIONS } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmRegOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_OPERAND } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmRegSpec { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_REG_SPEC } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for AsmSym { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { kind == ASM_SYM } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + #[inline] + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} impl AstNode for AssocItemList { #[inline] fn can_cast(kind: SyntaxKind) -> bool { kind == ASSOC_ITEM_LIST } @@ -4268,6 +4617,84 @@ impl AstNode for Adt { } } } +impl From for AsmOperand { + #[inline] + fn from(node: AsmConst) -> AsmOperand { AsmOperand::AsmConst(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmLabel) -> AsmOperand { AsmOperand::AsmLabel(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmRegOperand) -> AsmOperand { AsmOperand::AsmRegOperand(node) } +} +impl From for AsmOperand { + #[inline] + fn from(node: AsmSym) -> AsmOperand { AsmOperand::AsmSym(node) } +} +impl AstNode for AsmOperand { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CONST | ASM_LABEL | ASM_REG_OPERAND | ASM_SYM) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CONST => AsmOperand::AsmConst(AsmConst { syntax }), + ASM_LABEL => AsmOperand::AsmLabel(AsmLabel { syntax }), + ASM_REG_OPERAND => AsmOperand::AsmRegOperand(AsmRegOperand { syntax }), + ASM_SYM => AsmOperand::AsmSym(AsmSym { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmOperand::AsmConst(it) => &it.syntax, + AsmOperand::AsmLabel(it) => &it.syntax, + AsmOperand::AsmRegOperand(it) => &it.syntax, + AsmOperand::AsmSym(it) => &it.syntax, + } + } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmClobberAbi) -> AsmPiece { AsmPiece::AsmClobberAbi(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOperandNamed) -> AsmPiece { AsmPiece::AsmOperandNamed(node) } +} +impl From for AsmPiece { + #[inline] + fn from(node: AsmOptions) -> AsmPiece { AsmPiece::AsmOptions(node) } +} +impl AstNode for AsmPiece { + #[inline] + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, ASM_CLOBBER_ABI | ASM_OPERAND_NAMED | ASM_OPTIONS) + } + #[inline] + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + ASM_CLOBBER_ABI => AsmPiece::AsmClobberAbi(AsmClobberAbi { syntax }), + ASM_OPERAND_NAMED => AsmPiece::AsmOperandNamed(AsmOperandNamed { syntax }), + ASM_OPTIONS => AsmPiece::AsmOptions(AsmOptions { syntax }), + _ => return None, + }; + Some(res) + } + #[inline] + fn syntax(&self) -> &SyntaxNode { + match self { + AsmPiece::AsmClobberAbi(it) => &it.syntax, + AsmPiece::AsmOperandNamed(it) => &it.syntax, + AsmPiece::AsmOptions(it) => &it.syntax, + } + } +} impl From for AssocItem { #[inline] fn from(node: Const) -> AssocItem { AssocItem::Const(node) } @@ -5803,7 +6230,8 @@ impl AstNode for AnyHasName { fn can_cast(kind: SyntaxKind) -> bool { matches!( kind, - CONST + ASM_OPERAND_NAMED + | CONST | CONST_PARAM | ENUM | FN @@ -5832,6 +6260,10 @@ impl AstNode for AnyHasName { #[inline] fn syntax(&self) -> &SyntaxNode { &self.syntax } } +impl From for AnyHasName { + #[inline] + fn from(node: AsmOperandNamed) -> AnyHasName { AnyHasName { syntax: node.syntax } } +} impl From for AnyHasName { #[inline] fn from(node: Const) -> AnyHasName { AnyHasName { syntax: node.syntax } } @@ -6072,6 +6504,16 @@ impl std::fmt::Display for Adt { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmPiece { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItem { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -6142,11 +6584,66 @@ impl std::fmt::Display for ArrayType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmClobberAbi { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmConst { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmDirSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AsmExpr { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AsmLabel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOperandExpr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOperandNamed { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOption { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmOptions { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegOperand { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmRegSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AsmSym { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AssocItemList { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs index abf1a1f38207..fcdc97ce3270 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs @@ -1162,7 +1162,7 @@ pub mod tokens { pub(super) static SOURCE_FILE: LazyLock> = LazyLock::new(|| { SourceFile::parse( - "const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, + "use crate::foo; const C: <()>::Item = ( true && true , true || true , 1 != 1, 2 == 2, 3 < 3, 4 <= 4, 5 > 5, 6 >= 6, !true, *p, &p , &mut p, async { let _ @ [] })\n;\n\nimpl A for B where: {}", Edition::CURRENT, ) }); @@ -1188,6 +1188,17 @@ pub mod tokens { .unwrap() } + pub fn crate_kw() -> SyntaxToken { + SOURCE_FILE + .tree() + .syntax() + .clone_for_update() + .descendants_with_tokens() + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == CRATE_KW) + .unwrap() + } + pub fn whitespace(text: &str) -> SyntaxToken { assert!(text.trim().is_empty()); let sf = SourceFile::parse(text, Edition::CURRENT).ok().unwrap(); diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs index 152b0cb98c2a..5d6aa4331b02 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ast/traits.rs @@ -75,6 +75,33 @@ pub trait HasAttrs: AstNode { fn has_atom_attr(&self, atom: &str) -> bool { self.attrs().filter_map(|x| x.as_simple_atom()).any(|x| x == atom) } + + /// Returns all attributes of this node, including inner attributes that may not be directly under this node + /// but under a child. + fn attrs_including_inner(self) -> impl Iterator + where + Self: Sized, + { + let inner_attrs_node = if let Some(it) = + support::child::(self.syntax()).and_then(|it| it.stmt_list()) + { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else if let Some(it) = support::child::(self.syntax()) { + Some(it.syntax) + } else { + None + }; + + self.attrs().chain(inner_attrs_node.into_iter().flat_map(|it| support::children(&it))) + } } pub trait HasDocComments: HasAttrs { diff --git a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs index 9e63448ce963..2184359f1d0c 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/hacks.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/hacks.rs @@ -8,10 +8,15 @@ use crate::{ast, AstNode}; pub fn parse_expr_from_str(s: &str, edition: Edition) -> Option { let s = s.trim(); - let file = ast::SourceFile::parse(&format!("const _: () = {s};"), edition); - let expr = file.syntax_node().descendants().find_map(ast::Expr::cast)?; - if expr.syntax().text() != s { - return None; - } - Some(expr) + + let file = ast::SourceFile::parse( + // Need a newline because the text may contain line comments. + &format!("const _: () = ({s}\n);"), + edition, + ); + let expr = file.syntax_node().descendants().find_map(ast::ParenExpr::cast)?; + // Can't check the text because the original text may contain whitespace and comments. + // Wrap in parentheses to better allow for verification. Of course, the real fix is + // to get rid of this hack. + expr.expr() } diff --git a/src/tools/rust-analyzer/crates/syntax/src/lib.rs b/src/tools/rust-analyzer/crates/syntax/src/lib.rs index b68374848b90..c1554c4b2942 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/lib.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/lib.rs @@ -40,6 +40,7 @@ pub mod ast; #[doc(hidden)] pub mod fuzz; pub mod hacks; +pub mod syntax_editor; pub mod ted; pub mod utils; @@ -65,7 +66,7 @@ pub use rowan::{ TokenAtOffset, WalkEvent, }; pub use rustc_lexer::unescape; -pub use smol_str::{format_smolstr, SmolStr, ToSmolStr}; +pub use smol_str::{format_smolstr, SmolStr, SmolStrBuilder, ToSmolStr}; /// `Parse` is the result of the parsing: a syntax tree and a collection of /// errors. diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs new file mode 100644 index 000000000000..eb114f5e5f1f --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs @@ -0,0 +1,611 @@ +//! Syntax Tree editor +//! +//! Inspired by Roslyn's [`SyntaxEditor`], but is temporarily built upon mutable syntax tree editing. +//! +//! [`SyntaxEditor`]: https://github.com/dotnet/roslyn/blob/43b0b05cc4f492fd5de00f6f6717409091df8daa/src/Workspaces/Core/Portable/Editing/SyntaxEditor.cs + +use std::{ + num::NonZeroU32, + ops::RangeInclusive, + sync::atomic::{AtomicU32, Ordering}, +}; + +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode, SyntaxToken}; + +mod edit_algo; +mod mapping; + +pub use mapping::{SyntaxMapping, SyntaxMappingBuilder}; + +#[derive(Debug)] +pub struct SyntaxEditor { + root: SyntaxNode, + changes: Vec, + mappings: SyntaxMapping, + annotations: Vec<(SyntaxElement, SyntaxAnnotation)>, +} + +impl SyntaxEditor { + /// Creates a syntax editor to start editing from `root` + pub fn new(root: SyntaxNode) -> Self { + Self { root, changes: vec![], mappings: SyntaxMapping::new(), annotations: vec![] } + } + + pub fn add_annotation(&mut self, element: impl Element, annotation: SyntaxAnnotation) { + self.annotations.push((element.syntax_element(), annotation)) + } + + pub fn merge(&mut self, mut other: SyntaxEditor) { + debug_assert!( + self.root == other.root || other.root.ancestors().any(|node| node == self.root), + "{:?} is not in the same tree as {:?}", + other.root, + self.root + ); + + self.changes.append(&mut other.changes); + self.mappings.merge(other.mappings); + self.annotations.append(&mut other.annotations); + } + + pub fn insert(&mut self, position: Position, element: impl Element) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); + self.changes.push(Change::Insert(position, element.syntax_element())) + } + + pub fn insert_all(&mut self, position: Position, elements: Vec) { + debug_assert!(is_ancestor_or_self(&position.parent(), &self.root)); + self.changes.push(Change::InsertAll(position, elements)) + } + + pub fn delete(&mut self, element: impl Element) { + let element = element.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&element, &self.root)); + debug_assert!( + !matches!(&element, SyntaxElement::Node(node) if node == &self.root), + "should not delete root node" + ); + self.changes.push(Change::Replace(element.syntax_element(), None)); + } + + pub fn replace(&mut self, old: impl Element, new: impl Element) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); + self.changes.push(Change::Replace(old.syntax_element(), Some(new.syntax_element()))); + } + + pub fn replace_with_many(&mut self, old: impl Element, new: Vec) { + let old = old.syntax_element(); + debug_assert!(is_ancestor_or_self_of_element(&old, &self.root)); + debug_assert!( + !(matches!(&old, SyntaxElement::Node(node) if node == &self.root) && new.len() > 1), + "cannot replace root node with many elements" + ); + self.changes.push(Change::ReplaceWithMany(old.syntax_element(), new)); + } + + pub fn replace_all(&mut self, range: RangeInclusive, new: Vec) { + if range.start() == range.end() { + self.replace_with_many(range.start(), new); + return; + } + + debug_assert!(is_ancestor_or_self_of_element(range.start(), &self.root)); + self.changes.push(Change::ReplaceAll(range, new)) + } + + pub fn finish(self) -> SyntaxEdit { + edit_algo::apply_edits(self) + } +} + +/// Represents a completed [`SyntaxEditor`] operation. +pub struct SyntaxEdit { + old_root: SyntaxNode, + new_root: SyntaxNode, + changed_elements: Vec, + annotations: FxHashMap>, +} + +impl SyntaxEdit { + /// Root of the initial unmodified syntax tree. + pub fn old_root(&self) -> &SyntaxNode { + &self.old_root + } + + /// Root of the modified syntax tree. + pub fn new_root(&self) -> &SyntaxNode { + &self.new_root + } + + /// Which syntax elements in the modified syntax tree were inserted or + /// modified as part of the edit. + /// + /// Note that for syntax nodes, only the upper-most parent of a set of + /// changes is included, not any child elements that may have been modified. + pub fn changed_elements(&self) -> &[SyntaxElement] { + self.changed_elements.as_slice() + } + + /// Finds which syntax elements have been annotated with the given + /// annotation. + /// + /// Note that an annotation might not appear in the modified syntax tree if + /// the syntax elements that were annotated did not make it into the final + /// syntax tree. + pub fn find_annotation(&self, annotation: SyntaxAnnotation) -> &[SyntaxElement] { + self.annotations.get(&annotation).as_ref().map_or(&[], |it| it.as_slice()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct SyntaxAnnotation(NonZeroU32); + +impl SyntaxAnnotation { + /// Creates a unique syntax annotation to attach data to. + pub fn new() -> Self { + static COUNTER: AtomicU32 = AtomicU32::new(1); + + // Only consistency within a thread matters, as SyntaxElements are !Send + let id = COUNTER.fetch_add(1, Ordering::Relaxed); + + Self(NonZeroU32::new(id).expect("syntax annotation id overflow")) + } +} + +impl Default for SyntaxAnnotation { + fn default() -> Self { + Self::new() + } +} + +/// Position describing where to insert elements +#[derive(Debug)] +pub struct Position { + repr: PositionRepr, +} + +impl Position { + pub(crate) fn parent(&self) -> SyntaxNode { + self.place().0 + } + + pub(crate) fn place(&self) -> (SyntaxNode, usize) { + match &self.repr { + PositionRepr::FirstChild(parent) => (parent.clone(), 0), + PositionRepr::After(child) => (child.parent().unwrap(), child.index() + 1), + } + } +} + +#[derive(Debug)] +enum PositionRepr { + FirstChild(SyntaxNode), + After(SyntaxElement), +} + +impl Position { + pub fn after(elem: impl Element) -> Position { + let repr = PositionRepr::After(elem.syntax_element()); + Position { repr } + } + + pub fn before(elem: impl Element) -> Position { + let elem = elem.syntax_element(); + let repr = match elem.prev_sibling_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(elem.parent().unwrap()), + }; + Position { repr } + } + + pub fn first_child_of(node: &(impl Into + Clone)) -> Position { + let repr = PositionRepr::FirstChild(node.clone().into()); + Position { repr } + } + + pub fn last_child_of(node: &(impl Into + Clone)) -> Position { + let node = node.clone().into(); + let repr = match node.last_child_or_token() { + Some(it) => PositionRepr::After(it), + None => PositionRepr::FirstChild(node), + }; + Position { repr } + } +} + +#[derive(Debug)] +enum Change { + /// Inserts a single element at the specified position. + Insert(Position, SyntaxElement), + /// Inserts many elements in-order at the specified position. + InsertAll(Position, Vec), + /// Represents both a replace single element and a delete element operation. + Replace(SyntaxElement, Option), + /// Replaces a single element with many elements. + ReplaceWithMany(SyntaxElement, Vec), + /// Replaces a range of elements with another list of elements. + /// Range will always have start != end. + ReplaceAll(RangeInclusive, Vec), +} + +impl Change { + fn target_range(&self) -> TextRange { + match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &target.repr { + PositionRepr::FirstChild(parent) => TextRange::at( + parent.first_child_or_token().unwrap().text_range().start(), + 0.into(), + ), + PositionRepr::After(child) => TextRange::at(child.text_range().end(), 0.into()), + }, + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => target.text_range(), + Change::ReplaceAll(range, _) => { + range.start().text_range().cover(range.end().text_range()) + } + } + } + + fn target_parent(&self) -> SyntaxNode { + match self { + Change::Insert(target, _) | Change::InsertAll(target, _) => target.parent(), + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => match target { + SyntaxElement::Node(target) => target.parent().unwrap_or_else(|| target.clone()), + SyntaxElement::Token(target) => target.parent().unwrap(), + }, + Change::ReplaceAll(target, _) => target.start().parent().unwrap(), + } + } + + fn change_kind(&self) -> ChangeKind { + match self { + Change::Insert(_, _) | Change::InsertAll(_, _) => ChangeKind::Insert, + Change::Replace(_, _) | Change::ReplaceWithMany(_, _) => ChangeKind::Replace, + Change::ReplaceAll(_, _) => ChangeKind::ReplaceRange, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +enum ChangeKind { + Insert, + ReplaceRange, + Replace, +} + +/// Utility trait to allow calling syntax editor functions with references or owned +/// nodes. Do not use outside of this module. +pub trait Element { + fn syntax_element(self) -> SyntaxElement; +} + +impl Element for &'_ E { + fn syntax_element(self) -> SyntaxElement { + self.clone().syntax_element() + } +} + +impl Element for SyntaxElement { + fn syntax_element(self) -> SyntaxElement { + self + } +} + +impl Element for SyntaxNode { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +impl Element for SyntaxToken { + fn syntax_element(self) -> SyntaxElement { + self.into() + } +} + +fn is_ancestor_or_self(node: &SyntaxNode, ancestor: &SyntaxNode) -> bool { + node == ancestor || node.ancestors().any(|it| &it == ancestor) +} + +fn is_ancestor_or_self_of_element(node: &SyntaxElement, ancestor: &SyntaxNode) -> bool { + matches!(node, SyntaxElement::Node(node) if node == ancestor) + || node.ancestors().any(|it| &it == ancestor) +} + +#[cfg(test)] +mod tests { + use expect_test::expect; + use itertools::Itertools; + + use crate::{ + ast::{self, make, HasName}, + AstNode, + }; + + use super::*; + + fn make_ident_pat( + editor: Option<&mut SyntaxEditor>, + ref_: bool, + mut_: bool, + name: ast::Name, + ) -> ast::IdentPat { + let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone()); + mapping.finish(editor); + } + + ast + } + + fn make_let_stmt( + editor: Option<&mut SyntaxEditor>, + pattern: ast::Pat, + ty: Option, + initializer: Option, + ) -> ast::LetStmt { + let ast = + make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update(); + + if let Some(editor) = editor { + let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone()); + mapping.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone()); + if let Some(input) = ty { + mapping.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone()); + } + if let Some(input) = initializer { + mapping + .map_node(input.syntax().clone(), ast.initializer().unwrap().syntax().clone()); + } + mapping.finish(editor); + } + + ast + } + + fn make_block_expr( + editor: Option<&mut SyntaxEditor>, + stmts: impl IntoIterator, + tail_expr: Option, + ) -> ast::BlockExpr { + let stmts = stmts.into_iter().collect_vec(); + let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec(); + + let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update(); + + if let Some((editor, stmt_list)) = editor.zip(ast.stmt_list()) { + let mut mapping = SyntaxMappingBuilder::new(stmt_list.syntax().clone()); + + mapping.map_children( + input.into_iter(), + stmt_list.statements().map(|it| it.syntax().clone()), + ); + + if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) { + mapping.map_node(input.syntax().clone(), output.syntax().clone()); + } + + mapping.finish(editor); + } + + ast + } + + #[test] + fn basic_usage() { + let root = make::match_arm( + [make::wildcard_pat().into()], + None, + make::expr_tuple([ + make::expr_bin_op( + make::expr_literal("2").into(), + ast::BinaryOp::ArithOp(ast::ArithOp::Add), + make::expr_literal("2").into(), + ), + make::expr_literal("true").into(), + ]), + ); + + let to_wrap = root.syntax().descendants().find_map(ast::TupleExpr::cast).unwrap(); + let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let name = make::name("var_name"); + let name_ref = make::name_ref("var_name").clone_for_update(); + + let placeholder_snippet = SyntaxAnnotation::new(); + editor.add_annotation(name.syntax(), placeholder_snippet); + editor.add_annotation(name_ref.syntax(), placeholder_snippet); + + let make_ident_pat = make_ident_pat(Some(&mut editor), false, false, name); + let make_let_stmt = make_let_stmt( + Some(&mut editor), + make_ident_pat.into(), + None, + Some(to_replace.clone().into()), + ); + let new_block = make_block_expr( + Some(&mut editor), + [make_let_stmt.into()], + Some(to_wrap.clone().into()), + ); + + editor.replace(to_replace.syntax(), name_ref.syntax()); + editor.replace(to_wrap.syntax(), new_block.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + _ => { + let var_name = 2 + 2; + (var_name, true) + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + + assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2); + assert!(edit + .annotations + .iter() + .flat_map(|(_, elements)| elements) + .all(|element| element.ancestors().any(|it| &it == edit.new_root()))) + } + + #[test] + fn test_insert_independent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + editor.insert( + Position::first_child_of(root.stmt_list().unwrap().syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ) + .syntax(), + ); + + editor.insert( + Position::after(second_let.syntax()), + make_let_stmt( + None, + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ) + .syntax(), + ); + + let edit = editor.finish(); + + let expect = expect![[r#" + let first = 1;{ + let second = 2;let third = 3; + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + + #[test] + fn test_insert_dependent() { + let root = make::block_expr( + [], + Some( + make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ) + .into(), + ), + ); + + let inner_block = + root.syntax().descendants().flat_map(ast::BlockExpr::cast).nth(1).unwrap(); + let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + let third_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("third")).into(), + None, + Some(make::expr_literal("3").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.insert(Position::after(second_let.syntax()), third_let.syntax()); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + { + let first = 1;{ + let second = 2;let third = 3; + } + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } + + #[test] + fn test_replace_root_with_dependent() { + let root = make::block_expr( + [make::let_stmt( + make::ext::simple_ident_pat(make::name("second")).into(), + None, + Some(make::expr_literal("2").into()), + ) + .into()], + None, + ); + + let inner_block = root.clone(); + + let mut editor = SyntaxEditor::new(root.syntax().clone()); + + let new_block_expr = + make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone()))); + + let first_let = make_let_stmt( + Some(&mut editor), + make::ext::simple_ident_pat(make::name("first")).into(), + None, + Some(make::expr_literal("1").into()), + ); + + editor.insert( + Position::first_child_of(inner_block.stmt_list().unwrap().syntax()), + first_let.syntax(), + ); + editor.replace(inner_block.syntax(), new_block_expr.syntax()); + + let edit = editor.finish(); + + let expect = expect![[r#" + { + let first = 1;{ + let second = 2; + } + }"#]]; + expect.assert_eq(&edit.new_root.to_string()); + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs new file mode 100644 index 000000000000..b769c941105b --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/edit_algo.rs @@ -0,0 +1,367 @@ +//! Implementation of applying changes to a syntax tree. + +use std::{cmp::Ordering, collections::VecDeque, ops::RangeInclusive}; + +use rowan::TextRange; +use rustc_hash::FxHashMap; + +use crate::{ + syntax_editor::{mapping::MissingMapping, Change, ChangeKind, PositionRepr}, + SyntaxElement, SyntaxNode, SyntaxNodePtr, +}; + +use super::{SyntaxEdit, SyntaxEditor}; + +pub(super) fn apply_edits(editor: SyntaxEditor) -> SyntaxEdit { + // Algorithm overview: + // + // - Sort changes by (range, type) + // - Ensures that parent edits are before child edits + // - Ensures that inserts will be guaranteed to be inserted at the right range + // - Validate changes + // - Checking for invalid changes is easy since the changes will be sorted by range + // - Fixup change targets + // - standalone change? map to original syntax tree + // - dependent change? + // - try to map to parent change (either independent or another dependent) + // - note: need to keep track of a parent change stack, since a change can be a parent of multiple changes + // - Apply changes + // - find changes to apply to real tree by applying nested changes first + // - changed nodes become part of the changed node set (useful for the formatter to only change those parts) + // - Propagate annotations + + let SyntaxEditor { root, mut changes, mappings, annotations } = editor; + + let mut node_depths = FxHashMap::::default(); + let mut get_node_depth = |node: SyntaxNode| { + *node_depths.entry(node).or_insert_with_key(|node| node.ancestors().count()) + }; + + // Sort changes by range, then depth, then change kind, so that we can: + // - ensure that parent edits are ordered before child edits + // - ensure that inserts will be guaranteed to be inserted at the right range + // - easily check for disjoint replace ranges + changes.sort_by(|a, b| { + a.target_range() + .start() + .cmp(&b.target_range().start()) + .then_with(|| { + let a_target = a.target_parent(); + let b_target = b.target_parent(); + + if a_target == b_target { + return Ordering::Equal; + } + + get_node_depth(a_target).cmp(&get_node_depth(b_target)) + }) + .then(a.change_kind().cmp(&b.change_kind())) + }); + + let disjoint_replaces_ranges = changes + .iter() + .zip(changes.iter().skip(1)) + .filter(|(l, r)| { + // We only care about checking for disjoint replace ranges + matches!( + (l.change_kind(), r.change_kind()), + ( + ChangeKind::Replace | ChangeKind::ReplaceRange, + ChangeKind::Replace | ChangeKind::ReplaceRange + ) + ) + }) + .all(|(l, r)| { + get_node_depth(l.target_parent()) != get_node_depth(r.target_parent()) + || l.target_range().intersect(r.target_range()).is_none() + }); + + if stdx::never!( + !disjoint_replaces_ranges, + "some replace change ranges intersect: {:?}", + changes + ) { + return SyntaxEdit { + old_root: root.clone(), + new_root: root, + annotations: Default::default(), + changed_elements: vec![], + }; + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct DependentChange { + parent: u32, + child: u32, + } + + // Build change tree + let mut changed_ancestors: VecDeque = VecDeque::new(); + let mut dependent_changes = vec![]; + let mut independent_changes = vec![]; + + for (change_index, change) in changes.iter().enumerate() { + // Check if this change is dependent on another change (i.e. it's contained within another range) + if let Some(index) = changed_ancestors + .iter() + .rev() + .position(|ancestor| ancestor.affected_range().contains_range(change.target_range())) + { + // Pop off any ancestors that aren't applicable + changed_ancestors.drain((index + 1)..); + + // FIXME: Resolve changes that depend on a range of elements + let ancestor = &changed_ancestors[index]; + + dependent_changes.push(DependentChange { + parent: ancestor.change_index as u32, + child: change_index as u32, + }); + } else { + // This change is independent of any other change + + // Drain the changed ancestors since we're no longer in a set of dependent changes + changed_ancestors.drain(..); + + independent_changes.push(change_index as u32); + } + + // Add to changed ancestors, if applicable + match change { + Change::Insert(_, _) | Change::InsertAll(_, _) => {} + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + changed_ancestors.push_back(ChangedAncestor::single(target, change_index)) + } + Change::ReplaceAll(range, _) => { + changed_ancestors.push_back(ChangedAncestor::multiple(range, change_index)) + } + } + } + + // Map change targets to the correct syntax nodes + let tree_mutator = TreeMutator::new(&root); + let mut changed_elements = vec![]; + + for index in independent_changes { + match &mut changes[index as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => { + match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = tree_mutator.make_syntax_mut(parent); + } + PositionRepr::After(child) => { + *child = tree_mutator.make_element_mut(child); + } + }; + } + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + *target = tree_mutator.make_element_mut(target); + } + Change::ReplaceAll(range, _) => { + let start = tree_mutator.make_element_mut(range.start()); + let end = tree_mutator.make_element_mut(range.end()); + + *range = start..=end; + } + } + + // Collect changed elements + match &changes[index as usize] { + Change::Insert(_, element) => changed_elements.push(element.clone()), + Change::InsertAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + Change::Replace(_, Some(element)) => changed_elements.push(element.clone()), + Change::Replace(_, None) => {} + Change::ReplaceWithMany(_, elements) => { + changed_elements.extend(elements.iter().cloned()) + } + Change::ReplaceAll(_, elements) => changed_elements.extend(elements.iter().cloned()), + } + } + + for DependentChange { parent, child } in dependent_changes.into_iter() { + let (input_ancestor, output_ancestor) = match &changes[parent as usize] { + // No change will depend on an insert since changes can only depend on nodes in the root tree + Change::Insert(_, _) | Change::InsertAll(_, _) => unreachable!(), + Change::Replace(target, Some(new_target)) => { + (to_owning_node(target), to_owning_node(new_target)) + } + // Silently drop outdated change + Change::Replace(_, None) => continue, + Change::ReplaceAll(_, _) | Change::ReplaceWithMany(_, _) => { + unimplemented!("cannot resolve changes that depend on replacing many elements") + } + }; + + let upmap_target_node = |target: &SyntaxNode| { + match mappings.upmap_child(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } + }; + + let upmap_target = |target: &SyntaxElement| { + match mappings.upmap_child_element(target, &input_ancestor, &output_ancestor) { + Ok(it) => it, + Err(MissingMapping(current)) => unreachable!("no mappings exist between {current:?} (ancestor of {input_ancestor:?}) and {output_ancestor:?}"), + } + }; + + match &mut changes[child as usize] { + Change::Insert(target, _) | Change::InsertAll(target, _) => match &mut target.repr { + PositionRepr::FirstChild(parent) => { + *parent = upmap_target_node(parent); + } + PositionRepr::After(child) => { + *child = upmap_target(child); + } + }, + Change::Replace(target, _) | Change::ReplaceWithMany(target, _) => { + *target = upmap_target(target); + } + Change::ReplaceAll(range, _) => { + *range = upmap_target(range.start())..=upmap_target(range.end()); + } + } + } + + // Apply changes + let mut root = tree_mutator.mutable_clone; + + for change in changes { + match change { + Change::Insert(position, element) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, vec![element]); + } + Change::InsertAll(position, elements) => { + let (parent, index) = position.place(); + parent.splice_children(index..index, elements); + } + Change::Replace(target, None) => { + target.detach(); + } + Change::Replace(SyntaxElement::Node(target), Some(new_target)) if target == root => { + root = new_target.into_node().expect("root node replacement should be a node"); + } + Change::Replace(target, Some(new_target)) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, vec![new_target]); + } + Change::ReplaceWithMany(target, elements) => { + let parent = target.parent().unwrap(); + parent.splice_children(target.index()..target.index() + 1, elements); + } + Change::ReplaceAll(range, elements) => { + let start = range.start().index(); + let end = range.end().index(); + let parent = range.start().parent().unwrap(); + parent.splice_children(start..end + 1, elements); + } + } + } + + // Propagate annotations + let annotations = annotations.into_iter().filter_map(|(element, annotation)| { + match mappings.upmap_element(&element, &root) { + // Needed to follow the new tree to find the resulting element + Some(Ok(mapped)) => Some((mapped, annotation)), + // Element did not need to be mapped + None => Some((element, annotation)), + // Element did not make it to the final tree + Some(Err(_)) => None, + } + }); + + let mut annotation_groups = FxHashMap::default(); + + for (element, annotation) in annotations { + annotation_groups.entry(annotation).or_insert(vec![]).push(element); + } + + SyntaxEdit { + old_root: tree_mutator.immutable, + new_root: root, + changed_elements, + annotations: annotation_groups, + } +} + +fn to_owning_node(element: &SyntaxElement) -> SyntaxNode { + match element { + SyntaxElement::Node(node) => node.clone(), + SyntaxElement::Token(token) => token.parent().unwrap().clone(), + } +} + +struct ChangedAncestor { + kind: ChangedAncestorKind, + change_index: usize, +} + +enum ChangedAncestorKind { + Single { node: SyntaxNode }, + Range { _changed_elements: RangeInclusive, _in_parent: SyntaxNode }, +} + +impl ChangedAncestor { + fn single(element: &SyntaxElement, change_index: usize) -> Self { + let kind = match element { + SyntaxElement::Node(node) => ChangedAncestorKind::Single { node: node.clone() }, + SyntaxElement::Token(token) => { + ChangedAncestorKind::Single { node: token.parent().unwrap() } + } + }; + + Self { kind, change_index } + } + + fn multiple(range: &RangeInclusive, change_index: usize) -> Self { + Self { + kind: ChangedAncestorKind::Range { + _changed_elements: range.clone(), + _in_parent: range.start().parent().unwrap(), + }, + change_index, + } + } + + fn affected_range(&self) -> TextRange { + match &self.kind { + ChangedAncestorKind::Single { node } => node.text_range(), + ChangedAncestorKind::Range { _changed_elements: changed_nodes, _in_parent: _ } => { + TextRange::new( + changed_nodes.start().text_range().start(), + changed_nodes.end().text_range().end(), + ) + } + } + } +} + +struct TreeMutator { + immutable: SyntaxNode, + mutable_clone: SyntaxNode, +} + +impl TreeMutator { + fn new(immutable: &SyntaxNode) -> TreeMutator { + let immutable = immutable.clone(); + let mutable_clone = immutable.clone_for_update(); + TreeMutator { immutable, mutable_clone } + } + + fn make_element_mut(&self, element: &SyntaxElement) -> SyntaxElement { + match element { + SyntaxElement::Node(node) => SyntaxElement::Node(self.make_syntax_mut(node)), + SyntaxElement::Token(token) => { + let parent = self.make_syntax_mut(&token.parent().unwrap()); + parent.children_with_tokens().nth(token.index()).unwrap() + } + } + } + + fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { + let ptr = SyntaxNodePtr::new(node); + ptr.to_node(&self.mutable_clone) + } +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs new file mode 100644 index 000000000000..9bb5e6d93382 --- /dev/null +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -0,0 +1,272 @@ +//! Maps syntax elements through disjoint syntax nodes. +//! +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] + +use itertools::Itertools; +use rustc_hash::FxHashMap; + +use crate::{SyntaxElement, SyntaxNode}; + +use super::SyntaxEditor; + +#[derive(Debug, Default)] +pub struct SyntaxMapping { + // important information to keep track of: + // node -> node + // token -> token (implicit in mappings) + // input parent -> output parent (for deep lookups) + + // mappings -> parents + entry_parents: Vec, + node_mappings: FxHashMap, +} + +impl SyntaxMapping { + pub fn new() -> Self { + Self::default() + } + + /// Like [`SyntaxMapping::upmap_child`] but for syntax elements. + pub fn upmap_child_element( + &self, + child: &SyntaxElement, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result { + match child { + SyntaxElement::Node(node) => { + self.upmap_child(node, input_ancestor, output_ancestor).map(SyntaxElement::Node) + } + SyntaxElement::Token(token) => { + let upmap_parent = + self.upmap_child(&token.parent().unwrap(), input_ancestor, output_ancestor)?; + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + Ok(element) + } + } + } + + /// Maps a child node of the input ancestor to the corresponding node in + /// the output ancestor. + pub fn upmap_child( + &self, + child: &SyntaxNode, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result { + debug_assert!( + child == input_ancestor + || child.ancestors().any(|ancestor| &ancestor == input_ancestor) + ); + + // Build a list mapping up to the first mappable ancestor + let to_first_upmap = if child != input_ancestor { + std::iter::successors(Some((child.index(), child.clone())), |(_, current)| { + let parent = current.parent().unwrap(); + + if &parent == input_ancestor { + return None; + } + + Some((parent.index(), parent)) + }) + .map(|(i, _)| i) + .collect::>() + } else { + vec![] + }; + + // Progressively up-map the input ancestor until we get to the output ancestor + let to_output_ancestor = if input_ancestor != output_ancestor { + self.upmap_to_ancestor(input_ancestor, output_ancestor)? + } else { + vec![] + }; + + let to_map_down = + to_output_ancestor.into_iter().rev().chain(to_first_upmap.into_iter().rev()); + + let mut target = output_ancestor.clone(); + + for index in to_map_down { + target = target + .children_with_tokens() + .nth(index) + .and_then(|it| it.into_node()) + .expect("equivalent ancestor node should be present in target tree"); + } + + debug_assert_eq!(child.kind(), target.kind()); + + Ok(target) + } + + fn upmap_to_ancestor( + &self, + input_ancestor: &SyntaxNode, + output_ancestor: &SyntaxNode, + ) -> Result, MissingMapping> { + let mut current = + self.upmap_node_single(input_ancestor).unwrap_or_else(|| input_ancestor.clone()); + let mut upmap_chain = vec![current.index()]; + + loop { + let Some(parent) = current.parent() else { break }; + + if &parent == output_ancestor { + return Ok(upmap_chain); + } + + current = match self.upmap_node_single(&parent) { + Some(next) => next, + None => parent, + }; + upmap_chain.push(current.index()); + } + + Err(MissingMapping(current)) + } + + pub fn upmap_element( + &self, + input: &SyntaxElement, + output_root: &SyntaxNode, + ) -> Option> { + match input { + SyntaxElement::Node(node) => { + Some(self.upmap_node(node, output_root)?.map(SyntaxElement::Node)) + } + SyntaxElement::Token(token) => { + let upmap_parent = match self.upmap_node(&token.parent().unwrap(), output_root)? { + Ok(it) => it, + Err(err) => return Some(Err(err)), + }; + + let element = upmap_parent.children_with_tokens().nth(token.index()).unwrap(); + debug_assert!( + element.as_token().is_some_and(|it| it.kind() == token.kind()), + "token upmapping mapped to the wrong node ({token:?} -> {element:?})" + ); + + Some(Ok(element)) + } + } + } + + pub fn upmap_node( + &self, + input: &SyntaxNode, + output_root: &SyntaxNode, + ) -> Option> { + // Try to follow the mapping tree, if it exists + let input_mapping = self.upmap_node_single(input); + let input_ancestor = + input.ancestors().find_map(|ancestor| self.upmap_node_single(&ancestor)); + + match (input_mapping, input_ancestor) { + (Some(input_mapping), _) => { + // A mapping exists at the input, follow along the tree + Some(self.upmap_child(&input_mapping, &input_mapping, output_root)) + } + (None, Some(input_ancestor)) => { + // A mapping exists at an ancestor, follow along the tree + Some(self.upmap_child(input, &input_ancestor, output_root)) + } + (None, None) => { + // No mapping exists at all, is the same position in the final tree + None + } + } + } + + pub fn merge(&mut self, mut other: SyntaxMapping) { + // Remap other's entry parents to be after the current list of entry parents + let remap_base: u32 = self.entry_parents.len().try_into().unwrap(); + + self.entry_parents.append(&mut other.entry_parents); + self.node_mappings.extend(other.node_mappings.into_iter().map(|(node, entry)| { + (node, MappingEntry { parent: entry.parent + remap_base, ..entry }) + })); + } + + /// Follows the input one step along the syntax mapping tree + fn upmap_node_single(&self, input: &SyntaxNode) -> Option { + let MappingEntry { parent, child_slot } = self.node_mappings.get(input)?; + + let output = self.entry_parents[*parent as usize] + .children_with_tokens() + .nth(*child_slot as usize) + .and_then(SyntaxElement::into_node) + .unwrap(); + + debug_assert_eq!(input.kind(), output.kind()); + Some(output) + } + + fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) { + let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping; + + let parent_entry: u32 = self.entry_parents.len().try_into().unwrap(); + self.entry_parents.push(parent_node); + + let node_entries = node_mappings + .into_iter() + .map(|(node, slot)| (node, MappingEntry { parent: parent_entry, child_slot: slot })); + + self.node_mappings.extend(node_entries); + } +} + +#[derive(Debug)] +pub struct SyntaxMappingBuilder { + parent_node: SyntaxNode, + node_mappings: Vec<(SyntaxNode, u32)>, +} + +impl SyntaxMappingBuilder { + pub fn new(parent_node: SyntaxNode) -> Self { + Self { parent_node, node_mappings: vec![] } + } + + pub fn map_node(&mut self, input: SyntaxNode, output: SyntaxNode) { + debug_assert_eq!(output.parent().as_ref(), Some(&self.parent_node)); + self.node_mappings.push((input, output.index() as u32)); + } + + pub fn map_children( + &mut self, + input: impl Iterator, + output: impl Iterator, + ) { + for pairs in input.zip_longest(output) { + let (input, output) = match pairs { + itertools::EitherOrBoth::Both(l, r) => (l, r), + itertools::EitherOrBoth::Left(_) => { + unreachable!("mapping more input nodes than there are output nodes") + } + itertools::EitherOrBoth::Right(_) => break, + }; + + self.map_node(input, output); + } + } + + pub fn finish(self, editor: &mut SyntaxEditor) { + editor.mappings.add_mapping(self); + } +} + +#[derive(Debug)] +pub struct MissingMapping(pub SyntaxNode); + +#[derive(Debug, Clone, Copy)] +struct MappingEntry { + parent: u32, + child_slot: u32, +} diff --git a/src/tools/rust-analyzer/crates/syntax/src/ted.rs b/src/tools/rust-analyzer/crates/syntax/src/ted.rs index 29788d05e845..8592df159755 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ted.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ted.rs @@ -147,6 +147,11 @@ pub fn append_child_raw(node: &(impl Into + Clone), child: impl Elem insert_raw(position, child); } +pub fn prepend_child(node: &(impl Into + Clone), child: impl Element) { + let position = Position::first_child_of(node); + insert(position, child); +} + fn ws_before(position: &Position, new: &SyntaxElement) -> Option { let prev = match &position.repr { PositionRepr::FirstChild(_) => return None, diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index 03e85a898ab0..593e31c2fbb8 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -2,8 +2,8 @@ use std::{iter, mem, str::FromStr, sync}; use base_db::{ - CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileChange, - FileSet, LangCrateOrigin, SourceRoot, SourceRootDatabase, Version, VfsPath, + CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, CrateWorkspaceData, Dependency, + Env, FileChange, FileSet, LangCrateOrigin, SourceRoot, SourceRootDatabase, Version, VfsPath, }; use cfg::CfgOptions; use hir_expand::{ @@ -13,7 +13,7 @@ use hir_expand::{ proc_macro::{ ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, ProcMacrosBuilder, }, - FileRange, + quote, FileRange, }; use intern::Symbol; use rustc_hash::FxHashMap; @@ -95,8 +95,10 @@ pub trait WithFixture: Default + ExpandDatabase + SourceRootDatabase + 'static { fn test_crate(&self) -> CrateId { let crate_graph = self.crate_graph(); let mut it = crate_graph.iter(); - let res = it.next().unwrap(); - assert!(it.next().is_none()); + let mut res = it.next().unwrap(); + while crate_graph[res].origin.is_lang() { + res = it.next().unwrap(); + } res } } @@ -352,23 +354,27 @@ impl ChangeFixture { }; roots.push(root); - let mut change = ChangeWithProcMacros { - source_change, - proc_macros: Some(proc_macros.build()), - toolchains: Some(iter::repeat(toolchain).take(crate_graph.len()).collect()), - target_data_layouts: Some( - iter::repeat(target_data_layout).take(crate_graph.len()).collect(), - ), - }; + let mut change = + ChangeWithProcMacros { source_change, proc_macros: Some(proc_macros.build()) }; change.source_change.set_roots(roots); + change.source_change.set_ws_data( + crate_graph + .iter() + .zip(iter::repeat(From::from(CrateWorkspaceData { + proc_macro_cwd: None, + data_layout: target_data_layout, + toolchain, + }))) + .collect(), + ); change.source_change.set_crate_graph(crate_graph); ChangeFixture { file_position, files, change } } } -fn default_test_proc_macros() -> [(String, ProcMacro); 5] { +fn default_test_proc_macros() -> [(String, ProcMacro); 6] { [ ( r#" @@ -445,6 +451,21 @@ pub fn shorten(input: TokenStream) -> TokenStream { disabled: false, }, ), + ( + r#" +#[proc_macro_attribute] +pub fn issue_18089(_attr: TokenStream, _item: TokenStream) -> TokenStream { + loop {} +} +"# + .into(), + ProcMacro { + name: Symbol::intern("issue_18089"), + kind: ProcMacroKind::Attr, + expander: sync::Arc::new(Issue18089ProcMacroExpander), + disabled: false, + }, + ), ] } @@ -565,11 +586,41 @@ impl ProcMacroExpander for IdentityProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { Ok(subtree.clone()) } } +// Expands to a macro_rules! macro, for issue #18089. +#[derive(Debug)] +struct Issue18089ProcMacroExpander; +impl ProcMacroExpander for Issue18089ProcMacroExpander { + fn expand( + &self, + subtree: &Subtree, + _: Option<&Subtree>, + _: &Env, + _: Span, + call_site: Span, + _: Span, + _: Option, + ) -> Result, ProcMacroExpansionError> { + let macro_name = &subtree.token_trees[1]; + Ok(quote! { call_site => + #[macro_export] + macro_rules! my_macro___ { + ($($token:tt)*) => {{ + }}; + } + + pub use my_macro___ as #macro_name; + + #subtree + }) + } +} + // Pastes the attribute input as its output #[derive(Debug)] struct AttributeInputReplaceProcMacroExpander; @@ -582,6 +633,7 @@ impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { attrs .cloned() @@ -600,6 +652,7 @@ impl ProcMacroExpander for MirrorProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { fn traverse(input: &Subtree) -> Subtree { let mut token_trees = vec![]; @@ -630,6 +683,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander { _: Span, _: Span, _: Span, + _: Option, ) -> Result, ProcMacroExpansionError> { return Ok(traverse(input)); diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs index 3be4469beef0..67629fcf7cc5 100644 --- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs +++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs @@ -33,6 +33,7 @@ //! from: sized //! future: pin //! coroutine: pin +//! dispatch_from_dyn: unsize, pin //! hash: //! include: //! index: sized @@ -64,6 +65,7 @@ //! todo: panic //! unimplemented: panic //! column: +//! addr_of: #![rustc_coherence_is_core] @@ -169,7 +171,7 @@ pub mod default { macro_rules! impl_default { ($v:literal; $($t:ty)*) => { $( - impl const Default for $t { + impl Default for $t { fn default() -> Self { $v } @@ -420,6 +422,17 @@ pub mod ptr { } // endregion:coerce_unsized // endregion:non_null + + // region:addr_of + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of($place:expr) { + &raw const $place + } + #[rustc_macro_transparency = "semitransparent"] + pub macro addr_of_mut($place:expr) { + &raw mut $place + } + // endregion:addr_of } pub mod ops { @@ -673,7 +686,7 @@ pub mod ops { // endregion:fn // region:try mod try_ { - use super::super::convert::Infallible; + use crate::convert::Infallible; pub enum ControlFlow { #[lang = "Continue"] @@ -743,7 +756,7 @@ pub mod ops { // endregion:option // region:result // region:from - use super::super::convert::From; + use crate::convert::From; impl Try for Result { type Output = T; @@ -764,7 +777,7 @@ pub mod ops { impl> FromResidual> for Result { fn from_residual(residual: Result) -> Self { match residual { - Err(e) => Err(From::from(e)), + Err(e) => Err(F::from(e)), Ok(_) => loop {}, } } @@ -822,6 +835,24 @@ pub mod ops { } pub use self::coroutine::{Coroutine, CoroutineState}; // endregion:coroutine + + // region:dispatch_from_dyn + mod dispatch_from_dyn { + use crate::marker::Unsize; + + #[lang = "dispatch_from_dyn"] + pub trait DispatchFromDyn {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a U> for &'a T {} + + impl<'a, T: ?Sized + Unsize, U: ?Sized> DispatchFromDyn<&'a mut U> for &'a mut T {} + + impl, U: ?Sized> DispatchFromDyn<*const U> for *const T {} + + impl, U: ?Sized> DispatchFromDyn<*mut U> for *mut T {} + } + pub use self::dispatch_from_dyn::DispatchFromDyn; + // endregion:dispatch_from_dyn } // region:eq @@ -1183,6 +1214,12 @@ pub mod pin { } } // endregion:deref + // region:dispatch_from_dyn + impl crate::ops::DispatchFromDyn> for Pin where + Ptr: crate::ops::DispatchFromDyn + { + } + // endregion:dispatch_from_dyn } // endregion:pin @@ -1309,7 +1346,10 @@ pub mod iter { self } // region:iterators - fn take(self, n: usize) -> crate::iter::Take { + fn take(self, n: usize) -> crate::iter::Take + where + Self: Sized, + { loop {} } fn filter_map(self, _f: F) -> crate::iter::FilterMap @@ -1435,6 +1475,19 @@ mod panicking { } // endregion:panic +// region:asm +mod arch { + #[rustc_builtin_macro] + pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } + #[rustc_builtin_macro] + pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) { + /* compiler built-in */ + } +} +// endregion:asm + #[macro_use] mod macros { // region:panic @@ -1447,16 +1500,6 @@ mod macros { } // endregion:panic - // region:asm - #[macro_export] - #[rustc_builtin_macro] - macro_rules! asm { - ($($arg:tt)*) => { - /* compiler built-in */ - }; - } - // endregion:asm - // region:assert #[macro_export] #[rustc_builtin_macro] diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs index e96bed0319e1..587b903aa97a 100644 --- a/src/tools/rust-analyzer/crates/tt/src/iter.rs +++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs @@ -57,6 +57,13 @@ impl<'a, S: Copy> TtIter<'a, S> { } } + pub fn expect_comma(&mut self) -> Result<(), ()> { + match self.expect_leaf()? { + Leaf::Punct(Punct { char: ',', .. }) => Ok(()), + _ => Err(()), + } + } + pub fn expect_ident(&mut self) -> Result<&'a Ident, ()> { match self.expect_leaf()? { Leaf::Ident(it) if it.sym != sym::underscore => Ok(it), diff --git a/src/tools/rust-analyzer/crates/vfs/src/lib.rs b/src/tools/rust-analyzer/crates/vfs/src/lib.rs index bc40e03c5a24..a26444e9ea23 100644 --- a/src/tools/rust-analyzer/crates/vfs/src/lib.rs +++ b/src/tools/rust-analyzer/crates/vfs/src/lib.rs @@ -67,7 +67,7 @@ pub struct FileId(u32); // pub struct FileId(NonMaxU32); impl FileId { - pub const MAX: u32 = 0x7fff_ffff; + const MAX: u32 = 0x7fff_ffff; #[inline] pub const fn from_raw(raw: u32) -> FileId { diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md index 4786bd54d59b..b7bac4d29fad 100644 --- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md +++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ $DIR/nested-associated-type-bound-incompleteness.rs:24:21 + | +LL | drop::>(vec_u); + | --------------- ^^^^^ expected `Vec`, found `Vec` + | | + | arguments to this function are incorrect + | + = note: expected struct `Vec` + found struct `Vec` +note: function defined here + --> $SRC_DIR/core/src/mem/mod.rs:LL:COL + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/associated-type-bounds/nested-gat-projection.rs b/tests/ui/associated-type-bounds/nested-gat-projection.rs new file mode 100644 index 000000000000..ad37da9ed194 --- /dev/null +++ b/tests/ui/associated-type-bounds/nested-gat-projection.rs @@ -0,0 +1,31 @@ +//@ check-pass + +trait Trait +where + for<'a> Self::Gat<'a>: OtherTrait, + for<'a, 'b, 'c> as OtherTrait>::OtherGat<'b>: HigherRanked<'c>, +{ + type Gat<'a>; +} + +trait OtherTrait { + type OtherGat<'b>; +} + +trait HigherRanked<'c> {} + +fn lower_ranked OtherTrait: HigherRanked<'c>>>() {} + +fn higher_ranked() +where + for<'a> T::Gat<'a>: OtherTrait, + for<'a, 'b, 'c> as OtherTrait>::OtherGat<'b>: HigherRanked<'c>, +{ +} + +fn test() { + lower_ranked::>(); + higher_ranked::(); +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs new file mode 100644 index 000000000000..864c31893504 --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds-2.rs @@ -0,0 +1,28 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait Trait +where + Self::Assoc: Clone, +{ + type Assoc; +} + +fn foo(x: &T::Assoc) -> T::Assoc { + x.clone() +} + +trait Trait2 +where + Self::Assoc: Iterator, + ::Item: Clone, +{ + type Assoc; +} + +fn foo2(x: &::Item) -> ::Item { + x.clone() +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs new file mode 100644 index 000000000000..4e3b0b3b148a --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds-for-gat.rs @@ -0,0 +1,19 @@ +//@ check-pass + +// Test that `for<'a> Self::Gat<'a>: Debug` is implied in the definition of `Foo`, +// just as it would be if it weren't a GAT but just a regular associated type. + +use std::fmt::Debug; + +trait Foo +where + for<'a> Self::Gat<'a>: Debug, +{ + type Gat<'a>; +} + +fn test(x: T::Gat<'static>) { + println!("{:?}", x); +} + +fn main() {} diff --git a/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs b/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs new file mode 100644 index 000000000000..5a477a5b3494 --- /dev/null +++ b/tests/ui/associated-types/imply-relevant-nested-item-bounds.rs @@ -0,0 +1,23 @@ +//@ check-pass +//@ revisions: current next +//@[next] compile-flags: -Znext-solver + +trait Foo +where + Self::Iterator: Iterator, + ::Item: Bar, +{ + type Iterator; + + fn iter() -> Self::Iterator; +} + +trait Bar { + fn bar(&self); +} + +fn x() { + T::iter().next().unwrap().bar(); +} + +fn main() {} diff --git a/tests/ui/codegen/virtual-function-elimination.rs b/tests/ui/codegen/virtual-function-elimination.rs new file mode 100644 index 000000000000..3cbeb1293e50 --- /dev/null +++ b/tests/ui/codegen/virtual-function-elimination.rs @@ -0,0 +1,17 @@ +//@ build-pass +//@ compile-flags: -Zvirtual-function-elimination=true -Clto=true +//@ only-x86_64 +//@ no-prefer-dynamic + +// issue #123955 +pub fn test0() { + _ = Box::new(()) as Box; +} + +// issue #124092 +const X: for<'b> fn(&'b ()) = |&()| (); +pub fn test1() { + let _dyn_debug = Box::new(X) as Box as Box; +} + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr index db93bcca60fb..bae8249845c0 100644 --- a/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr +++ b/tests/ui/const-generics/generic_const_exprs/unify-op-with-fn-call.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/unify-op-with-fn-call.rs:3:12 + | +LL | #![feature(generic_const_exprs, adt_const_params, const_trait_impl, effects)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error: const `impl` for trait `Add` which is not marked with `#[const_trait]` --> $DIR/unify-op-with-fn-call.rs:10:12 | @@ -67,7 +75,7 @@ error[E0284]: type annotations needed: cannot normalize `foo2::{constant#0}` LL | bar2::<{ std::ops::Add::add(N, N) }>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot normalize `foo2::{constant#0}` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors Some errors have detailed explanations: E0284, E0741. For more information about an error, try `rustc --explain E0284`. diff --git a/tests/ui/const-generics/issues/issue-88119.stderr b/tests/ui/const-generics/issues/issue-88119.stderr index b5eec3046fd9..98bb81968100 100644 --- a/tests/ui/const-generics/issues/issue-88119.stderr +++ b/tests/ui/const-generics/issues/issue-88119.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/issue-88119.rs:4:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0284]: type annotations needed: cannot normalize `<&T as ConstName>::{constant#0}` --> $DIR/issue-88119.rs:19:49 | @@ -28,6 +36,6 @@ LL | where LL | [(); name_len::()]:, | --------------------- unsatisfied trait bound introduced here -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/mir/validate/validate-unsize-cast.rs b/tests/ui/mir/validate/validate-unsize-cast.rs new file mode 100644 index 000000000000..198af8d2e13a --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+Inline,+GVN -Zvalidate-mir + +#![feature(unsize)] + +use std::marker::Unsize; + +pub trait CastTo: Unsize {} + +// Not well-formed! +impl CastTo for T {} +//~^ ERROR the trait bound `T: Unsize` is not satisfied + +pub trait Cast { + fn cast(&self) + where + Self: CastTo; +} +impl Cast for T { + #[inline(always)] + fn cast(&self) + where + Self: CastTo, + { + let x: &U = self; + } +} + +fn main() { + // When we inline this call, then we run GVN, then + // GVN tries to evaluate the `() -> [i32]` unsize. + // That's invalid! + ().cast::<[i32]>(); +} diff --git a/tests/ui/mir/validate/validate-unsize-cast.stderr b/tests/ui/mir/validate/validate-unsize-cast.stderr new file mode 100644 index 000000000000..cfb47b34e980 --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `T: Unsize` is not satisfied + --> $DIR/validate-unsize-cast.rs:10:42 + | +LL | impl CastTo for T {} + | ^ the trait `Unsize` is not implemented for `T` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +note: required by a bound in `CastTo` + --> $DIR/validate-unsize-cast.rs:7:30 + | +LL | pub trait CastTo: Unsize {} + | ^^^^^^^^^ required by this bound in `CastTo` +help: consider further restricting this bound + | +LL | impl, U: ?Sized> CastTo for T {} + | ++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr index 7db6a77c77bf..698b1b5b5781 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/const-trait-bounds.stderr @@ -1,3 +1,11 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/const-trait-bounds.rs:4:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0284]: type annotations needed: cannot normalize `process::{constant#0}` --> $DIR/const-trait-bounds.rs:12:35 | @@ -16,6 +24,6 @@ error[E0284]: type annotations needed: cannot normalize `process::{constant#1 LL | input | ^^^^^ cannot normalize `process::{constant#1}` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0284`. diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs index c50c755f667f..5fffe54fc1a9 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.rs @@ -1,3 +1,4 @@ +//@ known-bug: unknown // Ensure that we print unsatisfied always-const trait bounds as `const Trait` in diagnostics. //@ compile-flags: -Znext-solver @@ -19,7 +20,7 @@ impl Trait for Ty { fn main() { // FIXME(effects): improve diagnostics on this - require::(); //~ ERROR the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied + require::(); } struct Container; @@ -27,9 +28,7 @@ struct Container; // FIXME(effects): Somehow emit `the trait bound `T: const Trait` is not satisfied` here instead // and suggest changing `Trait` to `const Trait`. fn accept0(_: Container<{ T::make() }>) {} -//~^ ERROR mismatched types // FIXME(effects): Instead of suggesting `+ const Trait`, suggest // changing `~const Trait` to `const Trait`. const fn accept1(_: Container<{ T::make() }>) {} -//~^ ERROR mismatched types diff --git a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr index b9f6c9e88356..0806ffa4b5df 100644 --- a/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr +++ b/tests/ui/rfcs/rfc-2632-const-trait-impl/unsatisfied-const-trait-bound.stderr @@ -1,5 +1,13 @@ +error: `-Znext-solver=globally` and `generic_const_exprs` are incompatible, using them at the same time is not allowed + --> $DIR/unsatisfied-const-trait-bound.rs:5:39 + | +LL | #![feature(const_trait_impl, effects, generic_const_exprs)] + | ^^^^^^^^^^^^^^^^^^^ + | + = help: remove one of these features + error[E0308]: mismatched types - --> $DIR/unsatisfied-const-trait-bound.rs:29:37 + --> $DIR/unsatisfied-const-trait-bound.rs:30:37 | LL | fn accept0(_: Container<{ T::make() }>) {} | ^^^^^^^^^ expected `false`, found `true` @@ -17,18 +25,18 @@ LL | const fn accept1(_: Container<{ T::make() }>) {} found constant `host` error[E0277]: the trait bound `Trait::{synthetic#0}: const Compat` is not satisfied - --> $DIR/unsatisfied-const-trait-bound.rs:22:15 + --> $DIR/unsatisfied-const-trait-bound.rs:23:15 | LL | require::(); | ^^ the trait `const Compat` is not implemented for `Trait::{synthetic#0}` | note: required by a bound in `require` - --> $DIR/unsatisfied-const-trait-bound.rs:7:15 + --> $DIR/unsatisfied-const-trait-bound.rs:8:15 | LL | fn require() {} | ^^^^^^^^^^^ required by this bound in `require` -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors Some errors have detailed explanations: E0277, E0308. For more information about an error, try `rustc --explain E0277`.