diff --git a/.typos.toml b/.typos.toml index e7e764ce0351..febfb233bd97 100644 --- a/.typos.toml +++ b/.typos.toml @@ -15,6 +15,7 @@ extend-ignore-re = [ '"flate2"', "raison d'être", "inout", + "INOUT", "optin" ] diff --git a/Cargo.lock b/Cargo.lock index 4e9319f13aa8..dc820fcb28d2 100644 --- a/Cargo.lock +++ b/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/Cargo.toml b/Cargo.toml index aa7bd2dc5fe8..b4587a379618 100644 --- a/Cargo.toml +++ b/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/crates/base-db/src/change.rs b/crates/base-db/src/change.rs index a9d91d64ceb6..4fb6654b612f 100644 --- a/crates/base-db/src/change.rs +++ b/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/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 3616fa9fd868..f5109339ad10 100644 --- a/crates/base-db/src/input.rs +++ b/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/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs index 20ef45d0b372..46e258d46f5c 100644 --- a/crates/base-db/src/lib.rs +++ b/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/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index dde1f142ab0c..9535b5aea7c7 100644 --- a/crates/hir-def/src/body.rs +++ b/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/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index abf789582920..9c547574ecb1 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/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/crates/hir-def/src/body/lower/asm.rs b/crates/hir-def/src/body/lower/asm.rs new file mode 100644 index 000000000000..4213370ac195 --- /dev/null +++ b/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/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs index 55740a68acd6..37167fcb8155 100644 --- a/crates/hir-def/src/body/pretty.rs +++ b/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/crates/hir-def/src/body/tests.rs b/crates/hir-def/src/body/tests.rs index 38fc01d8fca5..dd3e79c874d8 100644 --- a/crates/hir-def/src/body/tests.rs +++ b/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/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs index a70710e565c2..ba54451e594f 100644 --- a/crates/hir-def/src/data/adt.rs +++ b/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/crates/hir-def/src/hir.rs b/crates/hir-def/src/hir.rs index 86fd092603ff..d9358a28822e 100644 --- a/crates/hir-def/src/hir.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs index b6dbba12cd6d..a3b48831a0b0 100644 --- a/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index fc1460870cea..85fb90fdfb69 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs b/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs index bf7011983876..1430e2a9a994 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/metavar_expr.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs index 485f72e92ce1..1bbed01443de 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/mod.rs b/crates/hir-def/src/macro_expansion_tests/mod.rs index 7f761192517c..450a15bd66e5 100644 --- a/crates/hir-def/src/macro_expansion_tests/mod.rs +++ b/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/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs index 6f605c0cb33d..c0178adc9a63 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/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/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 11601c683e1e..9bd7d38f0a64 100644 --- a/crates/hir-def/src/nameres.rs +++ b/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/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 96db3db8f0d8..e09ef4f205d3 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/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/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs index 19485c476f72..3f3b98c6b5b5 100644 --- a/crates/hir-def/src/per_ns.rs +++ b/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/crates/hir-expand/src/builtin.rs b/crates/hir-expand/src/builtin.rs index 252430e4e954..7b9b7f36e2cd 100644 --- a/crates/hir-expand/src/builtin.rs +++ b/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/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs index 795d9b14df96..d04225b87227 100644 --- a/crates/hir-expand/src/builtin/fn_macro.rs +++ b/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/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs index 5c33f817f9e4..418d8d9660b5 100644 --- a/crates/hir-expand/src/builtin/quote.rs +++ b/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/crates/hir-expand/src/cfg_process.rs b/crates/hir-expand/src/cfg_process.rs index 147cf912da1e..01a3103af82d 100644 --- a/crates/hir-expand/src/cfg_process.rs +++ b/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/crates/hir-expand/src/change.rs b/crates/hir-expand/src/change.rs index 8b3f69db0277..de3a7b9f5615 100644 --- a/crates/hir-expand/src/change.rs +++ b/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/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs index b1a6eed2fbc7..86dd4aef090f 100644 --- a/crates/hir-expand/src/declarative.rs +++ b/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/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index 19c3c9c43f10..95380979492a 100644 --- a/crates/hir-expand/src/lib.rs +++ b/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/crates/hir-expand/src/prettify_macro_expansion_.rs b/crates/hir-expand/src/prettify_macro_expansion_.rs new file mode 100644 index 000000000000..d928cafdefc0 --- /dev/null +++ b/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/crates/hir-expand/src/proc_macro.rs b/crates/hir-expand/src/proc_macro.rs index 26bb3a3edda4..fe09f0307c9e 100644 --- a/crates/hir-expand/src/proc_macro.rs +++ b/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/crates/hir-ty/src/chalk_db.rs b/crates/hir-ty/src/chalk_db.rs index a3e4da5d1afd..e74e3d789883 100644 --- a/crates/hir-ty/src/chalk_db.rs +++ b/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/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 8b6cde975f63..968a828e9dfe 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/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/crates/hir-ty/src/consteval/tests.rs b/crates/hir-ty/src/consteval/tests.rs index 86228250c200..7093fcadcb03 100644 --- a/crates/hir-ty/src/consteval/tests.rs +++ b/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/crates/hir-ty/src/consteval/tests/intrinsics.rs b/crates/hir-ty/src/consteval/tests/intrinsics.rs index 5972b80d1696..c5706172b20c 100644 --- a/crates/hir-ty/src/consteval/tests/intrinsics.rs +++ b/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/crates/hir-ty/src/db.rs b/crates/hir-ty/src/db.rs index 9a1f2158bf75..ce5a821ea2bc 100644 --- a/crates/hir-ty/src/db.rs +++ b/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/crates/hir-ty/src/diagnostics/decl_check.rs b/crates/hir-ty/src/diagnostics/decl_check.rs index 024fc32f8637..82517e699175 100644 --- a/crates/hir-ty/src/diagnostics/decl_check.rs +++ b/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/crates/hir-ty/src/diagnostics/unsafe_check.rs b/crates/hir-ty/src/diagnostics/unsafe_check.rs index 3f54cdd20cee..ff45c725c73c 100644 --- a/crates/hir-ty/src/diagnostics/unsafe_check.rs +++ b/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/crates/hir-ty/src/generics.rs b/crates/hir-ty/src/generics.rs index a96c101a388b..89ca707c2e69 100644 --- a/crates/hir-ty/src/generics.rs +++ b/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/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 062ea278151b..8bc3c50725d9 100644 --- a/crates/hir-ty/src/infer.rs +++ b/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/crates/hir-ty/src/infer/cast.rs b/crates/hir-ty/src/infer/cast.rs index 060b5f36f292..caa3960a227c 100644 --- a/crates/hir-ty/src/infer/cast.rs +++ b/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 self.expr_ty.contains_unknown() || self.cast_ty.contains_unknown() { + return Ok(()); + } + + 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(), + }); + } - if table.coerce(&expr_ty, &cast_ty).is_ok() { - return; + // 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 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 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(()); } - // FIXME: Check other kinds of non-coercion casts and report error if any? + 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; +#[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, }; - table.coerce(expr_elt_ty, cast_inner_ty).is_ok() + + 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/crates/hir-ty/src/infer/closure.rs b/crates/hir-ty/src/infer/closure.rs index 36327d1d49c0..5cad08b93956 100644 --- a/crates/hir-ty/src/infer/closure.rs +++ b/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/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 89d92ea9af0b..a04e7b17ae6e 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/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/crates/hir-ty/src/infer/mutability.rs b/crates/hir-ty/src/infer/mutability.rs index 7fed5f0203ba..8e52725e5360 100644 --- a/crates/hir-ty/src/infer/mutability.rs +++ b/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/crates/hir-ty/src/infer/path.rs b/crates/hir-ty/src/infer/path.rs index 0b44bbec70f7..e4841c7b15b6 100644 --- a/crates/hir-ty/src/infer/path.rs +++ b/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/crates/hir-ty/src/infer/unify.rs b/crates/hir-ty/src/infer/unify.rs index c0f5ddddcbe3..7300453ff001 100644 --- a/crates/hir-ty/src/infer/unify.rs +++ b/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/crates/hir-ty/src/layout.rs b/crates/hir-ty/src/layout.rs index 25362d23d554..4cdc0db46a15 100644 --- a/crates/hir-ty/src/layout.rs +++ b/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/crates/hir-ty/src/layout/target.rs b/crates/hir-ty/src/layout/target.rs index 9b1424548c2a..7d77f6d0731a 100644 --- a/crates/hir-ty/src/layout/target.rs +++ b/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/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 26ab02558a1b..5ed41b99ba3e 100644 --- a/crates/hir-ty/src/lib.rs +++ b/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/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 370d9ba99cb0..c6c2108e34af 100644 --- a/crates/hir-ty/src/lower.rs +++ b/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/crates/hir-ty/src/mir.rs b/crates/hir-ty/src/mir.rs index 22d4da0e755a..8e815aabf207 100644 --- a/crates/hir-ty/src/mir.rs +++ b/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/crates/hir-ty/src/mir/eval.rs b/crates/hir-ty/src/mir/eval.rs index 1bb0c1886857..0d42617d185c 100644 --- a/crates/hir-ty/src/mir/eval.rs +++ b/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/crates/hir-ty/src/mir/eval/tests.rs b/crates/hir-ty/src/mir/eval/tests.rs index 371a5278dc9a..595a78da10fb 100644 --- a/crates/hir-ty/src/mir/eval/tests.rs +++ b/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/crates/hir-ty/src/mir/lower.rs b/crates/hir-ty/src/mir/lower.rs index 9e235504519e..a2cb122c5439 100644 --- a/crates/hir-ty/src/mir/lower.rs +++ b/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 - }) - } - // Enum to int casts - (TyKind::Scalar(_), TyKind::Adt(..)) | (TyKind::Adt(..), TyKind::Scalar(_)) => { - CastKind::IntToInt +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 } - (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/crates/hir-ty/src/object_safety.rs b/crates/hir-ty/src/object_safety.rs new file mode 100644 index 000000000000..a4c662685552 --- /dev/null +++ b/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/crates/hir-ty/src/object_safety/tests.rs b/crates/hir-ty/src/object_safety/tests.rs new file mode 100644 index 000000000000..c2a9117c5be4 --- /dev/null +++ b/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/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index 908bbc248c60..273571901ad5 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/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/crates/hir-ty/src/tests/macros.rs b/crates/hir-ty/src/tests/macros.rs index 5454a496ba8c..53b69c12f05d 100644 --- a/crates/hir-ty/src/tests/macros.rs +++ b/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/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 610fc9b6b456..74acf23b75ab 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/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/crates/hir-ty/src/tests/never_type.rs b/crates/hir-ty/src/tests/never_type.rs index 0ccbcf63e2b6..5c63cd00f97d 100644 --- a/crates/hir-ty/src/tests/never_type.rs +++ b/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/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 57866acc0637..51c27f8714ac 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/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/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 17fbe4db3179..a3cf12d8a16e 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/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/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 1c6fa62e30ce..0473ee02fab1 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/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/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index a98cff2a08b2..0b2d6bdd2593 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/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/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index ffb972475f8f..0b3cdb2f3790 100644 --- a/crates/hir/src/diagnostics.rs +++ b/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/crates/hir/src/display.rs b/crates/hir/src/display.rs index 923dca646673..c2b2fbef7517 100644 --- a/crates/hir/src/display.rs +++ b/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/crates/hir/src/has_source.rs b/crates/hir/src/has_source.rs index 7d52a28b91e9..82c90ac30101 100644 --- a/crates/hir/src/has_source.rs +++ b/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/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 58340c74fea9..8f5db32f9576 100644 --- a/crates/hir/src/lib.rs +++ b/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(_)))); + let mut result = if let Scalar::Int(_) = s { + value_signed.to_string() + } else { + value.to_string() + }; if value >= 10 { - return Ok(format!("{value_signed} ({value:#X})")); + format_to!(result, " ({value:#X})"); + return Ok(result); } else { - return Ok(format!("{value_signed}")); + 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/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 763f53031e4c..fa14b53dbc3d 100644 --- a/crates/hir/src/semantics.rs +++ b/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/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 09df639d4a81..c1e4e1d1e271 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/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/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index be0116862b9c..3da67ae23f83 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/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/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml index df52562b6a10..2a14fbe1e0a2 100644 --- a/crates/ide-assists/Cargo.toml +++ b/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/crates/ide-assists/src/handlers/add_missing_match_arms.rs b/crates/ide-assists/src/handlers/add_missing_match_arms.rs index b6abb06a2afc..a211ca8f2d67 100644 --- a/crates/ide-assists/src/handlers/add_missing_match_arms.rs +++ b/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/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs b/crates/ide-assists/src/handlers/explicit_enum_discriminant.rs new file mode 100644 index 000000000000..fafc3448a87c --- /dev/null +++ b/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/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index dcf16e89b2c4..1eaf31628f31 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/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/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 5ae75bb1ff8e..a43a4b5e1a06 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/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/crates/ide-assists/src/handlers/flip_comma.rs b/crates/ide-assists/src/handlers/flip_comma.rs index f40f2713ad13..af2c2c759ec7 100644 --- a/crates/ide-assists/src/handlers/flip_comma.rs +++ b/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/crates/ide-assists/src/handlers/generate_delegate_trait.rs b/crates/ide-assists/src/handlers/generate_delegate_trait.rs index bf4ce5c907e8..66bf9b018686 100644 --- a/crates/ide-assists/src/handlers/generate_delegate_trait.rs +++ b/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/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs index 821783c28313..7b7dac9a3d6c 100644 --- a/crates/ide-assists/src/handlers/generate_impl.rs +++ b/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/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 5bd204dd573b..9e09f198feb4 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/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/crates/ide-assists/src/handlers/inline_const_as_literal.rs b/crates/ide-assists/src/handlers/inline_const_as_literal.rs index f1c2acdd3ed9..6b504a918b40 100644 --- a/crates/ide-assists/src/handlers/inline_const_as_literal.rs +++ b/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/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 6a1f7f26c92c..b9fc075ae838 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/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/crates/ide-assists/src/handlers/inline_macro.rs b/crates/ide-assists/src/handlers/inline_macro.rs index 4708be616964..d558ec3bec7d 100644 --- a/crates/ide-assists/src/handlers/inline_macro.rs +++ b/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/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index f6624d6c872d..66dffde505c1 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/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/crates/ide-assists/src/handlers/introduce_named_generic.rs b/crates/ide-assists/src/handlers/introduce_named_generic.rs index 543b7f7ab632..bf6ac1719f31 100644 --- a/crates/ide-assists/src/handlers/introduce_named_generic.rs +++ b/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/crates/ide-assists/src/handlers/remove_unused_imports.rs b/crates/ide-assists/src/handlers/remove_unused_imports.rs index d4fdc072fb5b..c6f99d68748d 100644 --- a/crates/ide-assists/src/handlers/remove_unused_imports.rs +++ b/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/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs index 59bb0c45e143..a856da092153 100644 --- a/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs +++ b/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/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs b/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs index 65330b34c4ad..1101c20f1b78 100644 --- a/crates/ide-assists/src/handlers/replace_qualified_name_with_use.rs +++ b/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/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs index eedb2ea3b9ad..e452b5f77870 100644 --- a/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs +++ b/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/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs b/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs index b68ed00f7721..8f0e9b4fe09d 100644 --- a/crates/ide-assists/src/handlers/wrap_return_type_in_result.rs +++ b/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/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs index 0fa46ef43a1e..149cb4c43849 100644 --- a/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs +++ b/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/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index c88cb3d5eaf0..c98655b42302 100644 --- a/crates/ide-assists/src/lib.rs +++ b/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/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index dce7bbf342f7..48e12a810735 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/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/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index b8a6f3b6dbeb..0830017bd0fd 100644 --- a/crates/ide-assists/src/utils.rs +++ b/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/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index b537150608bb..414627fbabae 100644 --- a/crates/ide-completion/src/completions.rs +++ b/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/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 9821fb4a2fa9..d0b489c4e836 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/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/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index d55bc3ea5d03..f2c360a9d5bf 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/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/crates/ide-completion/src/completions/item_list/trait_impl.rs b/crates/ide-completion/src/completions/item_list/trait_impl.rs index fc6e1ebf05fc..672e1796d1ef 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/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/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 3f50cd55cb36..0acb87872f5e 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/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/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 60cfb7e5a8c5..8f38e02ed768 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/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/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index a632f148936d..d3579fd8cc6e 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/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/crates/ide-completion/src/config.rs b/crates/ide-completion/src/config.rs index d885b82ec90d..0d403f49b7a8 100644 --- a/crates/ide-completion/src/config.rs +++ b/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/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index bcd9df941947..192f1b43face 100644 --- a/crates/ide-completion/src/context.rs +++ b/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/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index ed359394f1c4..1f9e3edf625d 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/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/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index a30a115da1f1..8c97ebd55003 100644 --- a/crates/ide-completion/src/item.rs +++ b/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; - } - - 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 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; - } - } + 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, + }; + + // 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); + } - 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/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index ff5ec3a29f3e..f2e9de9382c6 100644 --- a/crates/ide-completion/src/render.rs +++ b/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/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 74092b53f523..a859d79e2433 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/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/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 04ba7e1f41b6..9d77d9700718 100644 --- a/crates/ide-completion/src/tests.rs +++ b/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/crates/ide-completion/src/tests/attribute.rs b/crates/ide-completion/src/tests/attribute.rs index 351abe9850b9..1bbe097cc6c8 100644 --- a/crates/ide-completion/src/tests/attribute.rs +++ b/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/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 8350fdcc4c07..0b532064fb2b 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/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/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index 8aad7bfc3adc..532d4928eff9 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/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/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 6a0b67e291af..bd3e7c72bcd6 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/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/crates/ide-completion/src/tests/raw_identifiers.rs b/crates/ide-completion/src/tests/raw_identifiers.rs index bc630189edc9..d81b3d697aa8 100644 --- a/crates/ide-completion/src/tests/raw_identifiers.rs +++ b/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/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index 2ae7d37889e5..508f6248dd41 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/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/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 5d4b1999088a..099f26eba78a 100644 --- a/crates/ide-db/src/defs.rs +++ b/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/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 3cf29987fa14..a45ff9a95455 100644 --- a/crates/ide-db/src/lib.rs +++ b/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/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 262eefeec00e..f1404ed9f22c 100644 --- a/crates/ide-db/src/rename.rs +++ b/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/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 12ce5a403fe8..4166b08339bf 100644 --- a/crates/ide-db/src/search.rs +++ b/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/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index a83f8473c396..73073e92f787 100644 --- a/crates/ide-db/src/source_change.rs +++ b/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/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-db/src/syntax_helpers/suggest_name.rs similarity index 70% rename from crates/ide-assists/src/utils/suggest_name.rs rename to crates/ide-db/src/syntax_helpers/suggest_name.rs index 3130ef069557..2679cbef61b3 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/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 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() -} - -/// Suggest name of impl trait type +/// The generator keeps track of existing names and suggests new names that do +/// not conflict with existing names. /// -/// `existing_params` is used to check if the name conflicts with existing -/// generic parameters. +/// The generator will try to resolve conflicts by adding a numeric suffix to +/// the name, e.g. `a`, `a1`, `a2`, ... /// -/// # Current implementation -/// -/// In current implementation, the function tries to get the name from the first -/// character of the name for the first type bound. +/// # Examples +/// ```rust +/// let mut generator = NameGenerator::new(); +/// assert_eq!(generator.suggest_name("a"), "a"); +/// assert_eq!(generator.suggest_name("a"), "a1"); /// -/// 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'); - - for_unique_generic_name(c.encode_utf8(&mut [0; 4]), existing_params) +/// assert_eq!(generator.suggest_name("b2"), "b2"); +/// assert_eq!(generator.suggest_name("b"), "b3"); +/// ``` +#[derive(Debug, Default)] +pub struct NameGenerator { + pool: FxHashMap, +} + +impl NameGenerator { + /// Create a new empty generator + pub fn new() -> Self { + Self { pool: FxHashMap::default() } + } + + /// 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/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index 18a95f0963dd..83a1eb44a616 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/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/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/crates/ide-diagnostics/src/handlers/invalid_cast.rs new file mode 100644 index 000000000000..ad4baf5e3a40 --- /dev/null +++ b/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/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/crates/ide-diagnostics/src/handlers/missing_unsafe.rs index af8ac6005d7a..5b43f4b2af3d 100644 --- a/crates/ide-diagnostics/src/handlers/missing_unsafe.rs +++ b/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/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs index 06c6b0f3e4c3..a9ff06fb0ab1 100644 --- a/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs +++ b/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/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e4b1f3ca9599..955427939150 100644 --- a/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/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/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs b/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs index 87932bf989f0..18647206236c 100644 --- a/crates/ide-diagnostics/src/handlers/replace_filter_map_next_with_find_map.rs +++ b/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/crates/ide-diagnostics/src/handlers/typed_hole.rs b/crates/ide-diagnostics/src/handlers/typed_hole.rs index b5c242e1e9f8..6994a7ed1465 100644 --- a/crates/ide-diagnostics/src/handlers/typed_hole.rs +++ b/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/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 9b50a435e4c3..45c723d09d4c 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/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; + 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 find_outline_mod_lint_severity( + sema: &Semantics<'_, RootDatabase>, + node: &InFile, + diag: &Diagnostic, + edition: Edition, +) -> 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); + } + }); + 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); } - 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(); - } + + 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); } } - 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, - ); + }); + + 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 }); + } + } + }); } - 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, - ); + + 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 parse_lint_attribute( - attr: ast::Attr, - rustc_stack: &mut FxHashMap>, - clippy_stack: &mut FxHashMap>, - job: impl Fn(&mut Vec, Severity), +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 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); + 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; } - 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); + } + + 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/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index 925ae620231d..ea16a11d56d0 100644 --- a/crates/ide/src/doc_links.rs +++ b/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/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index a939ed214ad8..79fdf75b7f7a 100644 --- a/crates/ide/src/expand_macro.rs +++ b/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/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 971cd3ef585c..8836166d969d 100644 --- a/crates/ide/src/goto_definition.rs +++ b/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/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index 5348e855be4b..4c8e3fc3040c 100644 --- a/crates/ide/src/highlight_related.rs +++ b/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/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 3e41b42be44b..83adf6548a89 100644 --- a/crates/ide/src/hover/render.rs +++ b/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/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 9585bdbe4c54..cca62d2181f4 100644 --- a/crates/ide/src/hover/tests.rs +++ b/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/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 6a5d5e26a4f0..97e712356b54 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/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/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs index 756198d0c011..c37c469dff4e 100644 --- a/crates/ide/src/inlay_hints/adjustment.rs +++ b/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/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index 82b0a6ffcf13..7a808fb4a929 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/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/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs index f27390ee898b..d1c0677863db 100644 --- a/crates/ide/src/inlay_hints/binding_mode.rs +++ b/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/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index 35f4d46e187c..58d8f97a8ced 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/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/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs index d78fd64bdf4d..90b8be64a46d 100644 --- a/crates/ide/src/inlay_hints/closing_brace.rs +++ b/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/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs index e87e10d8504e..f399bd01d071 100644 --- a/crates/ide/src/inlay_hints/closure_captures.rs +++ b/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/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs index 325c2040691b..6827540fa82c 100644 --- a/crates/ide/src/inlay_hints/closure_ret.rs +++ b/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/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs index eca0ebe629f4..35b62878329f 100644 --- a/crates/ide/src/inlay_hints/discriminant.rs +++ b/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/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs deleted file mode 100644 index d3666754e2be..000000000000 --- a/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/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs index b60a80a8ac6b..ed7ebc3b1e7a 100644 --- a/crates/ide/src/inlay_hints/generic_param.rs +++ b/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/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs index b4695a2b3519..dd4b3efeecfe 100644 --- a/crates/ide/src/inlay_hints/implicit_drop.rs +++ b/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/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs index 42223ddf580e..8d422478cbfc 100644 --- a/crates/ide/src/inlay_hints/implicit_static.rs +++ b/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/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs new file mode 100644 index 000000000000..2163c959b18a --- /dev/null +++ b/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/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs index 0f3142ef3f88..28b0fa6dd4d1 100644 --- a/crates/ide/src/inlay_hints/param_name.rs +++ b/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/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs index bfb92838857c..de9b0e98a4be 100644 --- a/crates/ide/src/inlay_hints/range_exclusive.rs +++ b/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/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index ba0aaae19c9f..547286c3f4d6 100644 --- a/crates/ide/src/lib.rs +++ b/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/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 4be1b570981f..14781b212969 100644 --- a/crates/ide/src/moniker.rs +++ b/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/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 9ace9fda62b9..9bc7bf411f05 100644 --- a/crates/ide/src/navigation_target.rs +++ b/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/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index 42b7472c645f..e46cb5a781f4 100644 --- a/crates/ide/src/rename.rs +++ b/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/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs index 518e71454798..7234108701a3 100644 --- a/crates/ide/src/syntax_highlighting/format.rs +++ b/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/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index eeba9cf35c99..96375937a12e 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/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/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index bc1ec530076c..5583f1bc8df9 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/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/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index e329023606a1..3b5d1af0ac72 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html new file mode 100644 index 000000000000..15a6386aa3c8 --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html new file mode 100644 index 000000000000..d07ba74db249 --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html new file mode 100644 index 000000000000..a790b385786d --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html new file mode 100644 index 000000000000..6dac066bfaa1 --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html new file mode 100644 index 000000000000..4ccc40799088 --- /dev/null +++ b/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/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html index cb47fc68bc1b..5594a36e7318 100644 --- a/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html @@ -165,16 +165,16 @@ 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/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 2070022d4183..94cee4ef43b1 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/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/crates/intern/src/symbol/symbols.rs b/crates/intern/src/symbol/symbols.rs index c066f5048915..a62cbc7fb29f 100644 --- a/crates/intern/src/symbol/symbols.rs +++ b/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/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs index abad7e9f7d22..baa45174236c 100644 --- a/crates/load-cargo/src/lib.rs +++ b/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/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index 756d42ef573f..b37d57f28012 100644 --- a/crates/mbe/Cargo.toml +++ b/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/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index 43604eb232dc..9e4ef1407408 100644 --- a/crates/mbe/src/benchmark.rs +++ b/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/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index e69d7d14e253..95641abc0f37 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/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/crates/mbe/src/expander/transcriber.rs b/crates/mbe/src/expander/transcriber.rs index 286bd748cbe7..1db2f35d2623 100644 --- a/crates/mbe/src/expander/transcriber.rs +++ b/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/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index 88785537c7d2..ca10a2be2732 100644 --- a/crates/mbe/src/lib.rs +++ b/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/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 218c04640f12..b55edf4a5e0c 100644 --- a/crates/mbe/src/parser.rs +++ b/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/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs new file mode 100644 index 000000000000..e63ad113ffd2 --- /dev/null +++ b/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/crates/parser/src/edition.rs b/crates/parser/src/edition.rs index be0a2c794e58..702b16252d4e 100644 --- a/crates/parser/src/edition.rs +++ b/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/crates/parser/src/grammar/expressions/atom.rs b/crates/parser/src/grammar/expressions/atom.rs index a678c1f3a70e..2333e6c862be 100644 --- a/crates/parser/src/grammar/expressions/atom.rs +++ b/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/crates/parser/src/lexed_str.rs b/crates/parser/src/lexed_str.rs index 3590486bd29e..dceac815e0b5 100644 --- a/crates/parser/src/lexed_str.rs +++ b/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/crates/parser/src/parser.rs b/crates/parser/src/parser.rs index 7d3eb5de25f9..f6b3783d1cac 100644 --- a/crates/parser/src/parser.rs +++ b/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/crates/parser/src/syntax_kind/generated.rs b/crates/parser/src/syntax_kind/generated.rs index 00f212487ae6..288a07ef44dc 100644 --- a/crates/parser/src/syntax_kind/generated.rs +++ b/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/crates/parser/test_data/generated/runner.rs b/crates/parser/test_data/generated/runner.rs index 9ce5a2ae748f..164d0f36f1be 100644 --- a/crates/parser/test_data/generated/runner.rs +++ b/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/crates/parser/test_data/parser/inline/ok/asm_expr.rast b/crates/parser/test_data/parser/inline/ok/asm_expr.rast new file mode 100644 index 000000000000..f0213d0b5e36 --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/ok/asm_expr.rs b/crates/parser/test_data/parser/inline/ok/asm_expr.rs new file mode 100644 index 000000000000..0906cd3e7197 --- /dev/null +++ b/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/crates/parser/test_data/parser/inline/ok/builtin_expr.rast b/crates/parser/test_data/parser/inline/ok/builtin_expr.rast index 361900b6d3e4..19a84ac54096 100644 --- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rast +++ b/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/crates/parser/test_data/parser/inline/ok/builtin_expr.rs b/crates/parser/test_data/parser/inline/ok/builtin_expr.rs index 14431b0210ea..920d0f794f24 100644 --- a/crates/parser/test_data/parser/inline/ok/builtin_expr.rs +++ b/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/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index d50a3cdbf72d..011baad65f7d 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/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/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index b8ac55ed0d5d..91bdef4889ca 100644 --- a/crates/project-model/src/lib.rs +++ b/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/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index a09c7a77abce..6a88cf022dfb 100644 --- a/crates/project-model/src/project_json.rs +++ b/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/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index f540bb94c196..30d1ddb636ef 100644 --- a/crates/project-model/src/tests.rs +++ b/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/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 7834238acefd..17b40a87cda9 100644 --- a/crates/project-model/src/workspace.rs +++ b/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/crates/project-model/test_data/regex-metadata.json b/crates/project-model/test_data/regex-metadata.json deleted file mode 100644 index 371464dd20aa..000000000000 --- a/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/crates/project-model/test_data/ripgrep-metadata.json b/crates/project-model/test_data/ripgrep-metadata.json deleted file mode 100644 index 131ff5dd7157..000000000000 --- a/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/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 21b481c1fa29..41b42573f082 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/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/crates/rust-analyzer/src/cli.rs b/crates/rust-analyzer/src/cli.rs index 5eb6ff664f66..a7ec5af89fcb 100644 --- a/crates/rust-analyzer/src/cli.rs +++ b/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/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 44e56645ba3c..e899e0e8eea5 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/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/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index cdac0e5ef5cc..28f25975d64a 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/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/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 16d90de661ad..73e71658d17c 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/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/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 89fe712ced02..e4263a3f667c 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/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/crates/rust-analyzer/src/cli/run_tests.rs b/crates/rust-analyzer/src/cli/run_tests.rs index 157ef43dd0a2..f90ebcfdb2e3 100644 --- a/crates/rust-analyzer/src/cli/run_tests.rs +++ b/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/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs index 75efdfd7dd8f..730f3c08abb5 100644 --- a/crates/rust-analyzer/src/cli/rustc_tests.rs +++ b/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/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index ceb8534fdf55..e9198977dea5 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/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/crates/rust-analyzer/src/cli/ssr.rs b/crates/rust-analyzer/src/cli/ssr.rs index 3caa48798874..bdca800a0d68 100644 --- a/crates/rust-analyzer/src/cli/ssr.rs +++ b/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/crates/rust-analyzer/src/cli/unresolved_references.rs b/crates/rust-analyzer/src/cli/unresolved_references.rs new file mode 100644 index 000000000000..986bd018b424 --- /dev/null +++ b/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/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2889af844b14..4cc60695fe66 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/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, - - /// 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, + /// Custom completion snippets. + completion_snippets_custom: FxHashMap = Config::completion_snippets_default(), - /// 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,51 +85,234 @@ 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()], + /// Enables highlighting of related references while the cursor is on `break`, `loop`, `while`, or `for` keywords. + highlightRelated_breakPoints_enable: bool = true, + /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. + highlightRelated_closureCaptures_enable: bool = true, + /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). + highlightRelated_exitPoints_enable: bool = true, + /// Enables highlighting of related references while the cursor is on any identifier. + highlightRelated_references_enable: bool = true, + /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. + highlightRelated_yieldPoints_enable: bool = true, - /// 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, + /// Whether to show `Debug` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_debug_enable: bool = true, + /// Whether to show HoverActions in Rust files. + hover_actions_enable: bool = true, + /// Whether to show `Go to Type Definition` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_gotoTypeDef_enable: bool = true, + /// Whether to show `Implementations` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_implementations_enable: bool = true, + /// Whether to show `References` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_references_enable: bool = false, + /// Whether to show `Run` action. Only applies when + /// `#rust-analyzer.hover.actions.enable#` is set. + hover_actions_run_enable: bool = true, + /// Whether to show documentation on hover. + hover_documentation_enable: bool = true, + /// Whether to show keyword hover popups. Only applies when + /// `#rust-analyzer.hover.documentation.enable#` is set. + hover_documentation_keywords_enable: bool = true, + /// Use markdown syntax for links on hover. + hover_links_enable: bool = true, + /// How to render the align information in a memory layout hover. + hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// Whether to show memory layout data on hover. + hover_memoryLayout_enable: bool = true, + /// How to render the niche information in a memory layout hover. + hover_memoryLayout_niches: Option = Some(false), + /// How to render the offset information in a memory layout hover. + hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), + /// How to render the size information in a memory layout hover. + hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. - /// + /// How many variants of an enum to display when hovering on. Show none if empty. + hover_show_enumVariants: Option = Some(5), + /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. + hover_show_fields: Option = Some(5), + /// How many associated items of a trait to display when hovering a trait. + hover_show_traitAssocItems: Option = None, + + /// Whether to show inlay type hints for binding modes. + inlayHints_bindingModeHints_enable: bool = false, + /// Whether to show inlay type hints for method chains. + inlayHints_chainingHints_enable: bool = true, + /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. + inlayHints_closingBraceHints_enable: bool = true, + /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 + /// to always show them). + inlayHints_closingBraceHints_minLines: usize = 25, + /// Whether to show inlay hints for closure captures. + inlayHints_closureCaptureHints_enable: bool = false, + /// Whether to show inlay type hints for return types of closures. + inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, + /// Closure notation in type and chaining inlay hints. + inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, + /// Whether to show enum variant discriminant hints. + inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, + /// Whether to show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, + /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. + inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, + /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). + inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, + /// Whether to show const generic parameter name inlay hints. + inlayHints_genericParameterHints_const_enable: bool= true, + /// Whether to show generic lifetime parameter name inlay hints. + inlayHints_genericParameterHints_lifetime_enable: bool = false, + /// Whether to show generic type parameter name inlay hints. + inlayHints_genericParameterHints_type_enable: bool = false, + /// Whether to show implicit drop hints. + inlayHints_implicitDrops_enable: bool = false, + /// Whether to show inlay type hints for elided lifetimes in function signatures. + inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, + /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. + inlayHints_lifetimeElisionHints_useParameterNames: bool = false, + /// Maximum length for inlay hints. Set to null to have an unlimited length. + inlayHints_maxLength: Option = Some(25), + /// Whether to show function parameter name inlay hints at the call + /// site. + inlayHints_parameterHints_enable: bool = true, + /// Whether to show exclusive range inlay hints. + inlayHints_rangeExclusiveHints_enable: bool = false, + /// Whether to show inlay hints for compiler inserted reborrows. + /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. + inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, + /// Whether to render leading colons for type hints, and trailing colons for parameter hints. + inlayHints_renderColons: bool = true, + /// Whether to show inlay type hints for variables. + inlayHints_typeHints_enable: bool = true, + /// Whether to hide inlay type hints for `let` statements that initialize to a closure. + /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. + inlayHints_typeHints_hideClosureInitialization: bool = false, + /// Whether to hide inlay type hints for constructors. + inlayHints_typeHints_hideNamedConstructor: bool = false, + + /// Enables the experimental support for interpreting tests. + interpret_tests: bool = false, + + /// Join lines merges consecutive declaration and initialization of an assignment. + joinLines_joinAssignments: bool = true, + /// Join lines inserts else between consecutive ifs. + joinLines_joinElseIf: bool = true, + /// Join lines removes trailing commas. + joinLines_removeTrailingComma: bool = true, + /// Join lines unwraps trivial blocks. + joinLines_unwrapTrivialBlock: bool = true, + + /// Whether to show `Debug` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_debug_enable: bool = true, + /// Whether to show CodeLens in Rust files. + lens_enable: bool = true, + /// Whether to show `Implementations` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_implementations_enable: bool = true, + /// Where to render annotations. + lens_location: AnnotationLocation = AnnotationLocation::AboveName, + /// Whether to show `References` lens for Struct, Enum, and Union. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_adt_enable: bool = false, + /// Whether to show `References` lens for Enum Variants. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_enumVariant_enable: bool = false, + /// Whether to show `Method References` lens. Only applies when + /// `#rust-analyzer.lens.enable#` is set. + lens_references_method_enable: bool = false, + /// Whether to show `References` lens for Trait. + /// Only applies when `#rust-analyzer.lens.enable#` is set. + lens_references_trait_enable: bool = false, + /// Whether to show `Run` lens. Only applies when + /// `#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, + + /// How many worker threads in the main loop. The default `null` means to pick automatically. + numThreads: Option = None, + + /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. + procMacro_attributes_enable: bool = true, + /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. + procMacro_enable: bool = true, + /// Internal config, path to proc-macro server executable. + procMacro_server: Option = None, + + /// Exclude imports from find-all-references. + references_excludeImports: bool = false, + + /// Exclude tests from find-all-references. + references_excludeTests: bool = false, + + /// Inject additional highlighting into doc comments. + /// + /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra + /// doc links. + semanticHighlighting_doc_comment_inject_enable: bool = true, + /// Whether the server is allowed to emit non-standard tokens and modifiers. + semanticHighlighting_nonStandardTokens: bool = true, + /// Use semantic tokens for operators. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when + /// they are tagged with modifiers. + semanticHighlighting_operator_enable: bool = true, + /// Use specialized semantic tokens for operators. + /// + /// When enabled, rust-analyzer will emit special token types for operator tokens instead + /// of the generic `operator` token type. + semanticHighlighting_operator_specialization_enable: bool = false, + /// Use semantic tokens for punctuation. + /// + /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when + /// they are tagged with modifiers or have a special role. + semanticHighlighting_punctuation_enable: bool = false, + /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro + /// calls. + semanticHighlighting_punctuation_separate_macro_bang: bool = false, + /// Use specialized semantic tokens for punctuation. + /// + /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead + /// of the generic `punctuation` token type. + semanticHighlighting_punctuation_specialization_enable: bool = false, + /// Use semantic tokens for strings. + /// + /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. + /// By disabling semantic tokens for strings, other grammars can be used to highlight + /// their contents. + semanticHighlighting_strings_enable: bool = true, + + /// Show full signature of the callable. Only shows parameters if disabled. + signatureInfo_detail: SignatureDetail = SignatureDetail::Full, + /// Show documentation. + signatureInfo_documentation_enable: bool = true, + + /// 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 @@ -408,6 +421,57 @@ config_data! { /// 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. @@ -429,303 +493,199 @@ config_data! { 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, - /// 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, - /// Enables highlighting of all captures of a closure while the cursor is on the `|` or move keyword of a closure. - highlightRelated_closureCaptures_enable: bool = true, - /// Enables highlighting of all exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). - highlightRelated_exitPoints_enable: bool = true, - /// Enables highlighting of related references while the cursor is on any identifier. - highlightRelated_references_enable: bool = true, - /// Enables highlighting of all break points for a loop or block context while the cursor is on any `async` or `await` keywords. - highlightRelated_yieldPoints_enable: bool = true, - - /// Whether to show `Debug` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_debug_enable: bool = true, - /// Whether to show HoverActions in Rust files. - hover_actions_enable: bool = true, - /// Whether to show `Go to Type Definition` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_gotoTypeDef_enable: bool = true, - /// Whether to show `Implementations` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_implementations_enable: bool = true, - /// Whether to show `References` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_references_enable: bool = false, - /// Whether to show `Run` action. Only applies when - /// `#rust-analyzer.hover.actions.enable#` is set. - hover_actions_run_enable: bool = true, - - /// Whether to show documentation on hover. - hover_documentation_enable: bool = true, - /// Whether to show keyword hover popups. Only applies when - /// `#rust-analyzer.hover.documentation.enable#` is set. - hover_documentation_keywords_enable: bool = true, - /// Use markdown syntax for links on hover. - hover_links_enable: bool = true, - /// How to render the align information in a memory layout hover. - hover_memoryLayout_alignment: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// Whether to show memory layout data on hover. - hover_memoryLayout_enable: bool = true, - /// How to render the niche information in a memory layout hover. - hover_memoryLayout_niches: Option = Some(false), - /// How to render the offset information in a memory layout hover. - hover_memoryLayout_offset: Option = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal), - /// How to render the size information in a memory layout hover. - hover_memoryLayout_size: Option = Some(MemoryLayoutHoverRenderKindDef::Both), - - /// How many variants of an enum to display when hovering on. Show none if empty. - hover_show_enumVariants: Option = Some(5), - /// How many fields of a struct, variant or union to display when hovering on. Show none if empty. - hover_show_fields: Option = Some(5), - /// How many associated items of a trait to display when hovering a trait. - hover_show_traitAssocItems: Option = None, - - /// Whether to show inlay type hints for binding modes. - inlayHints_bindingModeHints_enable: bool = false, - /// Whether to show inlay type hints for method chains. - inlayHints_chainingHints_enable: bool = true, - /// Whether to show inlay hints after a closing `}` to indicate what item it belongs to. - inlayHints_closingBraceHints_enable: bool = true, - /// Minimum number of lines required before the `}` until the hint is shown (set to 0 or 1 - /// to always show them). - inlayHints_closingBraceHints_minLines: usize = 25, - /// Whether to show inlay hints for closure captures. - inlayHints_closureCaptureHints_enable: bool = false, - /// Whether to show inlay type hints for return types of closures. - inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = ClosureReturnTypeHintsDef::Never, - /// Closure notation in type and chaining inlay hints. - inlayHints_closureStyle: ClosureStyle = ClosureStyle::ImplFn, - /// Whether to show enum variant discriminant hints. - inlayHints_discriminantHints_enable: DiscriminantHintsDef = DiscriminantHintsDef::Never, - /// Whether to show inlay hints for type adjustments. - inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = AdjustmentHintsDef::Never, - /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks. - inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = false, - /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). - inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = AdjustmentHintsModeDef::Prefix, - /// Whether to show const generic parameter name inlay hints. - inlayHints_genericParameterHints_const_enable: bool= true, - /// Whether to show generic lifetime parameter name inlay hints. - inlayHints_genericParameterHints_lifetime_enable: bool = false, - /// Whether to show generic type parameter name inlay hints. - inlayHints_genericParameterHints_type_enable: bool = false, - /// Whether to show implicit drop hints. - inlayHints_implicitDrops_enable: bool = false, - /// Whether to show inlay type hints for elided lifetimes in function signatures. - inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never, - /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. - inlayHints_lifetimeElisionHints_useParameterNames: bool = false, - /// Maximum length for inlay hints. Set to null to have an unlimited length. - inlayHints_maxLength: Option = Some(25), - /// Whether to show function parameter name inlay hints at the call - /// site. - inlayHints_parameterHints_enable: bool = true, - /// Whether to show exclusive range inlay hints. - inlayHints_rangeExclusiveHints_enable: bool = false, - /// Whether to show inlay hints for compiler inserted reborrows. - /// This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. - inlayHints_reborrowHints_enable: ReborrowHintsDef = ReborrowHintsDef::Never, - /// Whether to render leading colons for type hints, and trailing colons for parameter hints. - inlayHints_renderColons: bool = true, - /// Whether to show inlay type hints for variables. - inlayHints_typeHints_enable: bool = true, - /// Whether to hide inlay type hints for `let` statements that initialize to a closure. - /// Only applies to closures with blocks, same as `#rust-analyzer.inlayHints.closureReturnTypeHints.enable#`. - inlayHints_typeHints_hideClosureInitialization: bool = false, - /// Whether to hide inlay type hints for constructors. - inlayHints_typeHints_hideNamedConstructor: bool = false, - - /// Enables the experimental support for interpreting tests. - interpret_tests: bool = false, - - /// Join lines merges consecutive declaration and initialization of an assignment. - joinLines_joinAssignments: bool = true, - /// Join lines inserts else between consecutive ifs. - joinLines_joinElseIf: bool = true, - /// Join lines removes trailing commas. - joinLines_removeTrailingComma: bool = true, - /// Join lines unwraps trivial blocks. - joinLines_unwrapTrivialBlock: bool = true, - - /// Whether to show `Debug` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_debug_enable: bool = true, - /// Whether to show CodeLens in Rust files. - lens_enable: bool = true, - /// Whether to show `Implementations` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_implementations_enable: bool = true, - /// Where to render annotations. - lens_location: AnnotationLocation = AnnotationLocation::AboveName, - /// Whether to show `References` lens for Struct, Enum, and Union. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_adt_enable: bool = false, - /// Whether to show `References` lens for Enum Variants. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_enumVariant_enable: bool = false, - /// Whether to show `Method References` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_references_method_enable: bool = false, - /// Whether to show `References` lens for Trait. - /// Only applies when `#rust-analyzer.lens.enable#` is set. - lens_references_trait_enable: bool = false, - /// Whether to show `Run` lens. Only applies when - /// `#rust-analyzer.lens.enable#` is set. - lens_run_enable: bool = true, - - /// Whether to show `can't find Cargo.toml` error message. - notifications_cargoTomlNotFound: bool = true, - - /// How many worker threads in the main loop. The default `null` means to pick automatically. - numThreads: Option = None, - - /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set. - procMacro_attributes_enable: bool = true, - /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`. - procMacro_enable: bool = true, - /// Internal config, path to proc-macro server executable. - procMacro_server: Option = None, - - /// Exclude imports from find-all-references. - references_excludeImports: bool = false, + /// Run the check command for diagnostics on save. + checkOnSave | checkOnSave_enable: bool = true, - /// Exclude tests from find-all-references. - references_excludeTests: bool = false, - /// Inject additional highlighting into doc comments. + /// 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#`. /// - /// When enabled, rust-analyzer will highlight rust source in doc comments as well as intra - /// doc links. - semanticHighlighting_doc_comment_inject_enable: bool = true, - /// Whether the server is allowed to emit non-standard tokens and modifiers. - semanticHighlighting_nonStandardTokens: bool = true, - /// Use semantic tokens for operators. + /// 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. /// - /// When disabled, rust-analyzer will emit semantic tokens only for operator tokens when - /// they are tagged with modifiers. - semanticHighlighting_operator_enable: bool = true, - /// Use specialized semantic tokens for operators. + /// 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`). /// - /// When enabled, rust-analyzer will emit special token types for operator tokens instead - /// of the generic `operator` token type. - semanticHighlighting_operator_specialization_enable: bool = false, - /// Use semantic tokens for punctuation. + /// If you're changing this because you're using some tool wrapping + /// Cargo, you might also want to change + /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. /// - /// When disabled, rust-analyzer will emit semantic tokens only for punctuation tokens when - /// they are tagged with modifiers or have a special role. - semanticHighlighting_punctuation_enable: bool = false, - /// When enabled, rust-analyzer will emit a punctuation semantic token for the `!` of macro - /// calls. - semanticHighlighting_punctuation_separate_macro_bang: bool = false, - /// Use specialized semantic tokens for punctuation. + /// 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#`. /// - /// When enabled, rust-analyzer will emit special token types for punctuation tokens instead - /// of the generic `punctuation` token type. - semanticHighlighting_punctuation_specialization_enable: bool = false, - /// Use semantic tokens for strings. + /// 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. /// - /// In some editors (e.g. vscode) semantic tokens override other highlighting grammars. - /// By disabling semantic tokens for strings, other grammars can be used to highlight - /// their contents. - semanticHighlighting_strings_enable: bool = true, + /// 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, - /// Show full signature of the callable. Only shows parameters if disabled. - signatureInfo_detail: SignatureDetail = SignatureDetail::Full, - /// Show documentation. - signatureInfo_documentation_enable: 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, - /// Whether to insert closing angle brackets when typing an opening angle bracket of a generic argument list. - typing_autoClosingAngleBrackets_enable: bool = false, /// Workspace symbol search kind. workspace_symbol_search_kind: WorkspaceSymbolSearchKindDef = WorkspaceSymbolSearchKindDef::OnlyTypes, @@ -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() + fn discovered_projects(&self) -> Vec { + let exclude_dirs: Vec<_> = + self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).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; } - 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"); - Some(ProjectJson::new(None, root_path, data.clone()).into()) - } - ManifestOrProjectJson::ProjectJson(it) => { - Some(ProjectJson::new(None, &self.root_path, it.clone()).into()) - } - }) - .collect(), + 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/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 9d0082c370c7..89487aa673bf 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/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/crates/rust-analyzer/src/handlers/dispatch.rs b/crates/rust-analyzer/src/handlers/dispatch.rs index f03de8ce0f03..ed7bf27843b5 100644 --- a/crates/rust-analyzer/src/handlers/dispatch.rs +++ b/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/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 38b88ff2d040..49b1ba32a795 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/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/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs index 1ad5ff0c8cdc..bcbd970a0d21 100644 --- a/crates/rust-analyzer/src/handlers/request.rs +++ b/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/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 28f4b809d6c8..118469df730c 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/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/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 8d1a686dc4d4..618481bbc66b 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/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/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs index eb6bc2a9ce9b..4902c9f88c14 100644 --- a/crates/rust-analyzer/src/lsp/to_proto.rs +++ b/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/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 616d6b49351c..835592302576 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/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/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs index eab973387240..5c4c858e1509 100644 --- a/crates/rust-analyzer/src/op_queue.rs +++ b/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/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 68366136eda6..f6765715c5a1 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/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/crates/rust-analyzer/src/target_spec.rs b/crates/rust-analyzer/src/target_spec.rs index 954e13cbf272..b4aa73d2780d 100644 --- a/crates/rust-analyzer/src/target_spec.rs +++ b/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/crates/rust-analyzer/tests/crate_graph.rs b/crates/rust-analyzer/tests/crate_graph.rs deleted file mode 100644 index 04b6713b8d18..000000000000 --- a/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/crates/rust-analyzer/tests/slow-tests/ratoml.rs b/crates/rust-analyzer/tests/slow-tests/ratoml.rs index 295d1d4e8e90..a857e0c2967c 100644 --- a/crates/rust-analyzer/tests/slow-tests/ratoml.rs +++ b/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/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json b/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_A.json deleted file mode 100644 index b0fb5845cef7..000000000000 --- a/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/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json b/crates/rust-analyzer/tests/test_data/deduplication_crate_graph_B.json deleted file mode 100644 index b5d1e16e62e0..000000000000 --- a/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/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs index 874480c59fbf..cb9c092f5fcd 100644 --- a/crates/span/src/hygiene.rs +++ b/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/crates/span/src/map.rs b/crates/span/src/map.rs index c539754979d6..f80de05ec652 100644 --- a/crates/span/src/map.rs +++ b/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/crates/syntax-bridge/src/lib.rs b/crates/syntax-bridge/src/lib.rs index 56e43e82ed27..0ccd08867602 100644 --- a/crates/syntax-bridge/src/lib.rs +++ b/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/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs b/crates/syntax-bridge/src/prettify_macro_expansion.rs similarity index 85% rename from crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs rename to crates/syntax-bridge/src/prettify_macro_expansion.rs index dd4a665e8eb4..fc7caaa98865 100644 --- a/crates/ide-db/src/syntax_helpers/insert_whitespace_into_node.rs +++ b/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/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 43375ce6ae05..52ad439e4dec 100644 --- a/crates/syntax/rust.ungram +++ b/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/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index c9b39e9922c9..c81a19f3bda4 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/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/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index abf1a1f38207..fcdc97ce3270 100644 --- a/crates/syntax/src/ast/make.rs +++ b/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/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 152b0cb98c2a..5d6aa4331b02 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/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/crates/syntax/src/hacks.rs b/crates/syntax/src/hacks.rs index 9e63448ce963..2184359f1d0c 100644 --- a/crates/syntax/src/hacks.rs +++ b/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/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index b68374848b90..c1554c4b2942 100644 --- a/crates/syntax/src/lib.rs +++ b/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/crates/syntax/src/syntax_editor.rs b/crates/syntax/src/syntax_editor.rs new file mode 100644 index 000000000000..eb114f5e5f1f --- /dev/null +++ b/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/crates/syntax/src/syntax_editor/edit_algo.rs b/crates/syntax/src/syntax_editor/edit_algo.rs new file mode 100644 index 000000000000..b769c941105b --- /dev/null +++ b/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/crates/syntax/src/syntax_editor/mapping.rs b/crates/syntax/src/syntax_editor/mapping.rs new file mode 100644 index 000000000000..9bb5e6d93382 --- /dev/null +++ b/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/crates/syntax/src/ted.rs b/crates/syntax/src/ted.rs index 29788d05e845..8592df159755 100644 --- a/crates/syntax/src/ted.rs +++ b/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/crates/test-fixture/src/lib.rs b/crates/test-fixture/src/lib.rs index 03e85a898ab0..593e31c2fbb8 100644 --- a/crates/test-fixture/src/lib.rs +++ b/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/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index 3be4469beef0..67629fcf7cc5 100644 --- a/crates/test-utils/src/minicore.rs +++ b/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/crates/tt/src/iter.rs b/crates/tt/src/iter.rs index e96bed0319e1..587b903aa97a 100644 --- a/crates/tt/src/iter.rs +++ b/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/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index bc40e03c5a24..a26444e9ea23 100644 --- a/crates/vfs/src/lib.rs +++ b/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/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 4786bd54d59b..b7bac4d29fad 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@