From a1f1b95d0043e92a504510434e143962079ffaf2 Mon Sep 17 00:00:00 2001 From: Amos Wenger Date: Tue, 26 Jul 2022 11:53:50 +0200 Subject: [PATCH 01/22] Merge commit 'e36a20c24f35a4cee82bbdc600289104c9237c22' into ra-sync-and-pms-component --- Cargo.lock | 7 + crates/hir-def/src/body/lower.rs | 40 ++++- crates/hir-def/src/data.rs | 19 ++- crates/hir-def/src/expr.rs | 12 +- crates/hir-def/src/item_scope.rs | 35 +++-- crates/hir-def/src/lib.rs | 10 +- crates/hir-def/src/nameres.rs | 27 +++- crates/hir-def/src/nameres/collector.rs | 56 +++---- crates/hir-def/src/resolver.rs | 7 +- crates/hir-ty/src/diagnostics/expr.rs | 5 +- crates/hir-ty/src/infer/expr.rs | 18 +-- crates/hir/src/lib.rs | 26 ++++ crates/hir/src/semantics.rs | 138 +++++++++++------- crates/hir/src/semantics/source_to_def.rs | 2 + crates/hir/src/source_analyzer.rs | 71 +++++++-- .../src/handlers/add_missing_impl_members.rs | 17 ++- .../src/utils/gen_trait_fn_body.rs | 11 +- crates/ide-completion/src/item.rs | 1 + crates/ide-completion/src/render.rs | 40 ++++- crates/ide-db/src/defs.rs | 13 +- crates/ide-db/src/lib.rs | 1 + crates/ide-db/src/path_transform.rs | 3 +- crates/ide-db/src/rename.rs | 12 +- crates/ide-db/src/search.rs | 10 +- .../src/handlers/missing_fields.rs | 31 ++++ crates/ide/src/doc_links.rs | 6 +- crates/ide/src/hover.rs | 7 +- crates/ide/src/hover/render.rs | 1 + crates/ide/src/navigation_target.rs | 2 + crates/ide/src/signature_help.rs | 3 +- crates/ide/src/syntax_highlighting.rs | 8 + .../ide/src/syntax_highlighting/highlight.rs | 1 + crates/ide/src/syntax_highlighting/inject.rs | 1 + crates/ide/src/syntax_highlighting/tags.rs | 1 + crates/proc-macro-api/src/process.rs | 1 + crates/proc-macro-srv-cli/Cargo.toml | 17 +++ crates/proc-macro-srv-cli/src/main.rs | 19 +++ crates/proc-macro-test/build.rs | 3 +- crates/project-model/src/project_json.rs | 5 + crates/project-model/src/sysroot.rs | 21 ++- crates/project-model/src/tests.rs | 7 +- crates/project-model/src/workspace.rs | 24 ++- crates/rust-analyzer/src/cli/load_cargo.rs | 6 +- crates/rust-analyzer/src/global_state.rs | 4 +- crates/rust-analyzer/src/handlers.rs | 2 +- crates/rust-analyzer/src/reload.rs | 77 +++++++--- crates/rust-analyzer/src/semantic_tokens.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 5 +- 48 files changed, 624 insertions(+), 210 deletions(-) create mode 100644 crates/proc-macro-srv-cli/Cargo.toml create mode 100644 crates/proc-macro-srv-cli/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 4c830006832c..703f0e5b8af9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1198,6 +1198,13 @@ dependencies = [ "tt", ] +[[package]] +name = "proc-macro-srv-cli" +version = "0.0.0" +dependencies = [ + "proc-macro-srv", +] + [[package]] name = "proc-macro-test" version = "0.0.0" diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index c3f261122784..66f9c24e8724 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -96,6 +96,7 @@ pub(super) fn lower( expander, name_to_pat_grouping: Default::default(), is_lowering_inside_or_pat: false, + is_lowering_assignee_expr: false, } .collect(params, body) } @@ -109,6 +110,7 @@ struct ExprCollector<'a> { // a poor-mans union-find? name_to_pat_grouping: FxHashMap>, is_lowering_inside_or_pat: bool, + is_lowering_assignee_expr: bool, } impl ExprCollector<'_> { @@ -283,7 +285,10 @@ impl ExprCollector<'_> { } else { Box::default() }; - self.alloc_expr(Expr::Call { callee, args }, syntax_ptr) + self.alloc_expr( + Expr::Call { callee, args, is_assignee_expr: self.is_lowering_assignee_expr }, + syntax_ptr, + ) } ast::Expr::MethodCallExpr(e) => { let receiver = self.collect_expr_opt(e.receiver()); @@ -359,6 +364,7 @@ impl ExprCollector<'_> { ast::Expr::RecordExpr(e) => { let path = e.path().and_then(|path| self.expander.parse_path(self.db, path)).map(Box::new); + let is_assignee_expr = self.is_lowering_assignee_expr; let record_lit = if let Some(nfl) = e.record_expr_field_list() { let fields = nfl .fields() @@ -378,9 +384,16 @@ impl ExprCollector<'_> { }) .collect(); let spread = nfl.spread().map(|s| self.collect_expr(s)); - Expr::RecordLit { path, fields, spread } + let ellipsis = nfl.dotdot_token().is_some(); + Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr } } else { - Expr::RecordLit { path, fields: Box::default(), spread: None } + Expr::RecordLit { + path, + fields: Box::default(), + spread: None, + ellipsis: false, + is_assignee_expr, + } }; self.alloc_expr(record_lit, syntax_ptr) @@ -458,14 +471,21 @@ impl ExprCollector<'_> { ) } ast::Expr::BinExpr(e) => { + let op = e.op_kind(); + if let Some(ast::BinaryOp::Assignment { op: None }) = op { + self.is_lowering_assignee_expr = true; + } let lhs = self.collect_expr_opt(e.lhs()); + self.is_lowering_assignee_expr = false; let rhs = self.collect_expr_opt(e.rhs()); - let op = e.op_kind(); self.alloc_expr(Expr::BinaryOp { lhs, rhs, op }, syntax_ptr) } ast::Expr::TupleExpr(e) => { let exprs = e.fields().map(|expr| self.collect_expr(expr)).collect(); - self.alloc_expr(Expr::Tuple { exprs }, syntax_ptr) + self.alloc_expr( + Expr::Tuple { exprs, is_assignee_expr: self.is_lowering_assignee_expr }, + syntax_ptr, + ) } ast::Expr::BoxExpr(e) => { let expr = self.collect_expr_opt(e.expr()); @@ -477,8 +497,14 @@ impl ExprCollector<'_> { match kind { ArrayExprKind::ElementList(e) => { - let exprs = e.map(|expr| self.collect_expr(expr)).collect(); - self.alloc_expr(Expr::Array(Array::ElementList(exprs)), syntax_ptr) + let elements = e.map(|expr| self.collect_expr(expr)).collect(); + self.alloc_expr( + Expr::Array(Array::ElementList { + elements, + is_assignee_expr: self.is_lowering_assignee_expr, + }), + syntax_ptr, + ) } ArrayExprKind::Repeat { initializer, repeat } => { let initializer = self.collect_expr_opt(initializer); diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 430941141933..35c8708955a7 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -12,7 +12,7 @@ use crate::{ db::DefDatabase, intern::Interned, item_tree::{self, AssocItem, FnFlags, ItemTree, ItemTreeId, ModItem, Param, TreeId}, - nameres::{attr_resolution::ResolvedAttr, DefMap}, + nameres::{attr_resolution::ResolvedAttr, proc_macro::ProcMacroKind, DefMap}, type_ref::{TraitRef, TypeBound, TypeRef}, visibility::RawVisibility, AssocItemId, AstIdWithPath, ConstId, ConstLoc, FunctionId, FunctionLoc, HasModule, ImplId, @@ -348,7 +348,8 @@ impl MacroRulesData { #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProcMacroData { pub name: Name, - // FIXME: Record deriver helper here? + /// Derive helpers, if this is a derive + pub helpers: Option>, } impl ProcMacroData { @@ -360,17 +361,23 @@ impl ProcMacroData { let item_tree = loc.id.item_tree(db); let makro = &item_tree[loc.id.value]; - let name = if let Some(def) = item_tree + let (name, helpers) = if let Some(def) = item_tree .attrs(db, loc.container.krate(), ModItem::from(loc.id.value).into()) .parse_proc_macro_decl(&makro.name) { - def.name + ( + def.name, + match def.kind { + ProcMacroKind::CustomDerive { helpers } => Some(helpers), + ProcMacroKind::FnLike | ProcMacroKind::Attr => None, + }, + ) } else { // eeeh... stdx::never!("proc macro declaration is not a proc macro"); - makro.name.clone() + (makro.name.clone(), None) }; - Arc::new(ProcMacroData { name }) + Arc::new(ProcMacroData { name, helpers }) } } diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index a991365d6bf4..c1b3788acb7d 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -110,6 +110,7 @@ pub enum Expr { Call { callee: ExprId, args: Box<[ExprId]>, + is_assignee_expr: bool, }, MethodCall { receiver: ExprId, @@ -138,6 +139,8 @@ pub enum Expr { path: Option>, fields: Box<[RecordLitField]>, spread: Option, + ellipsis: bool, + is_assignee_expr: bool, }, Field { expr: ExprId, @@ -196,6 +199,7 @@ pub enum Expr { }, Tuple { exprs: Box<[ExprId]>, + is_assignee_expr: bool, }, Unsafe { body: ExprId, @@ -211,7 +215,7 @@ pub enum Expr { #[derive(Debug, Clone, Eq, PartialEq)] pub enum Array { - ElementList(Box<[ExprId]>), + ElementList { elements: Box<[ExprId]>, is_assignee_expr: bool }, Repeat { initializer: ExprId, repeat: ExprId }, } @@ -285,7 +289,7 @@ impl Expr { f(*iterable); f(*body); } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { f(*callee); args.iter().copied().for_each(f); } @@ -339,9 +343,9 @@ impl Expr { | Expr::Box { expr } => { f(*expr); } - Expr::Tuple { exprs } => exprs.iter().copied().for_each(f), + Expr::Tuple { exprs, .. } => exprs.iter().copied().for_each(f), Expr::Array(a) => match a { - Array::ElementList(exprs) => exprs.iter().copied().for_each(f), + Array::ElementList { elements, .. } => elements.iter().copied().for_each(f), Array::Repeat { initializer, repeat } => { f(*initializer); f(*repeat) diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index b98b2855cb08..579f803ea193 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -66,10 +66,14 @@ pub struct ItemScope { attr_macros: FxHashMap, MacroCallId>, /// The derive macro invocations in this scope, keyed by the owner item over the actual derive attributes /// paired with the derive macro invocations for the specific attribute. - derive_macros: FxHashMap< - AstId, - SmallVec<[(AttrId, MacroCallId, SmallVec<[Option; 1]>); 1]>, - >, + derive_macros: FxHashMap, SmallVec<[DeriveMacroInvocation; 1]>>, +} + +#[derive(Debug, PartialEq, Eq)] +struct DeriveMacroInvocation { + attr_id: AttrId, + attr_call_id: MacroCallId, + derive_call_ids: SmallVec<[Option; 1]>, } pub(crate) static BUILTIN_SCOPE: Lazy> = Lazy::new(|| { @@ -210,12 +214,14 @@ impl ItemScope { &mut self, adt: AstId, call: MacroCallId, - attr_id: AttrId, + id: AttrId, idx: usize, ) { if let Some(derives) = self.derive_macros.get_mut(&adt) { - if let Some((.., invocs)) = derives.iter_mut().find(|&&mut (id, ..)| id == attr_id) { - invocs[idx] = Some(call); + if let Some(DeriveMacroInvocation { derive_call_ids, .. }) = + derives.iter_mut().find(|&&mut DeriveMacroInvocation { attr_id, .. }| id == attr_id) + { + derive_call_ids[idx] = Some(call); } } } @@ -227,10 +233,14 @@ impl ItemScope { &mut self, adt: AstId, attr_id: AttrId, - call_id: MacroCallId, + attr_call_id: MacroCallId, len: usize, ) { - self.derive_macros.entry(adt).or_default().push((attr_id, call_id, smallvec![None; len])); + self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation { + attr_id, + attr_call_id, + derive_call_ids: smallvec![None; len], + }); } pub(crate) fn derive_macro_invocs( @@ -242,7 +252,12 @@ impl ItemScope { ), > + '_ { self.derive_macros.iter().map(|(k, v)| { - (*k, v.iter().map(|&(attr_id, call_id, ref invocs)| (attr_id, call_id, &**invocs))) + ( + *k, + v.iter().map(|DeriveMacroInvocation { attr_id, attr_call_id, derive_call_ids }| { + (*attr_id, *attr_call_id, &**derive_call_ids) + }), + ) }) } diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 0dd0a5861ef3..56603f4b1545 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -934,11 +934,11 @@ fn derive_macro_as_call_id( derive_attr: AttrId, derive_pos: u32, krate: CrateId, - resolver: impl Fn(path::ModPath) -> Option, -) -> Result { - let def: MacroDefId = resolver(item_attr.path.clone()) + resolver: impl Fn(path::ModPath) -> Option<(MacroId, MacroDefId)>, +) -> Result<(MacroId, MacroDefId, MacroCallId), UnresolvedMacro> { + let (macro_id, def_id) = resolver(item_attr.path.clone()) .ok_or_else(|| UnresolvedMacro { path: item_attr.path.clone() })?; - let res = def.as_lazy_macro( + let call_id = def_id.as_lazy_macro( db.upcast(), krate, MacroCallKind::Derive { @@ -947,7 +947,7 @@ fn derive_macro_as_call_id( derive_attr_index: derive_attr.ast_index, }, ); - Ok(res) + Ok((macro_id, def_id, call_id)) } fn attr_macro_as_call_id( diff --git a/crates/hir-def/src/nameres.rs b/crates/hir-def/src/nameres.rs index 756fd583af4d..6eb530ecc542 100644 --- a/crates/hir-def/src/nameres.rs +++ b/crates/hir-def/src/nameres.rs @@ -48,19 +48,19 @@ //! the result pub mod attr_resolution; -mod collector; +pub mod proc_macro; pub mod diagnostics; +mod collector; mod mod_resolution; mod path_resolution; -mod proc_macro; #[cfg(test)] mod tests; -use std::{cmp::Ord, sync::Arc}; +use std::{cmp::Ord, ops::Deref, sync::Arc}; use base_db::{CrateId, Edition, FileId}; -use hir_expand::{name::Name, InFile, MacroDefId}; +use hir_expand::{name::Name, InFile, MacroCallId, MacroDefId}; use itertools::Itertools; use la_arena::Arena; use profile::Count; @@ -76,7 +76,7 @@ use crate::{ path::ModPath, per_ns::PerNs, visibility::Visibility, - AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, ModuleId, ProcMacroId, + AstId, BlockId, BlockLoc, FunctionId, LocalModuleId, MacroId, ModuleId, ProcMacroId, }; /// Contains the results of (early) name resolution. @@ -106,6 +106,9 @@ pub struct DefMap { fn_proc_macro_mapping: FxHashMap, /// The error that occurred when failing to load the proc-macro dll. proc_macro_loading_error: Option>, + /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper + /// attributes. + derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, /// Custom attributes registered with `#![register_attr]`. registered_attrs: Vec, @@ -275,6 +278,7 @@ impl DefMap { exported_derives: FxHashMap::default(), fn_proc_macro_mapping: FxHashMap::default(), proc_macro_loading_error: None, + derive_helpers_in_scope: FxHashMap::default(), prelude: None, root, modules, @@ -294,12 +298,22 @@ impl DefMap { pub fn modules(&self) -> impl Iterator + '_ { self.modules.iter() } + + pub fn derive_helpers_in_scope( + &self, + id: AstId, + ) -> Option<&[(Name, MacroId, MacroCallId)]> { + self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) + } + pub fn registered_tools(&self) -> &[SmolStr] { &self.registered_tools } + pub fn registered_attrs(&self) -> &[SmolStr] { &self.registered_attrs } + pub fn root(&self) -> LocalModuleId { self.root } @@ -307,6 +321,7 @@ impl DefMap { pub fn fn_as_proc_macro(&self, id: FunctionId) -> Option { self.fn_proc_macro_mapping.get(&id).copied() } + pub fn proc_macro_loading_error(&self) -> Option<&str> { self.proc_macro_loading_error.as_deref() } @@ -463,6 +478,7 @@ impl DefMap { registered_attrs, registered_tools, fn_proc_macro_mapping, + derive_helpers_in_scope, proc_macro_loading_error: _, block: _, edition: _, @@ -479,6 +495,7 @@ impl DefMap { registered_attrs.shrink_to_fit(); registered_tools.shrink_to_fit(); fn_proc_macro_mapping.shrink_to_fit(); + derive_helpers_in_scope.shrink_to_fit(); for (_, module) in modules.iter_mut() { module.children.shrink_to_fit(); module.scope.shrink_to_fit(); diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 67651e06413c..f394c541719f 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -18,7 +18,7 @@ use hir_expand::{ ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, }; -use itertools::Itertools; +use itertools::{izip, Itertools}; use la_arena::Idx; use limit::Limit; use rustc_hash::{FxHashMap, FxHashSet}; @@ -110,7 +110,6 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: DefMap, tree_id: T proc_macros, from_glob_import: Default::default(), skip_attrs: Default::default(), - derive_helpers_in_scope: Default::default(), is_proc_macro, }; if tree_id.is_block() { @@ -258,9 +257,6 @@ struct DefCollector<'a> { /// This also stores the attributes to skip when we resolve derive helpers and non-macro /// non-builtin attributes in general. skip_attrs: FxHashMap, AttrId>, - /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper - /// attributes. - derive_helpers_in_scope: FxHashMap, Vec>, } impl DefCollector<'_> { @@ -1059,7 +1055,7 @@ impl DefCollector<'_> { }; let mut res = ReachedFixedPoint::Yes; macros.retain(|directive| { - let resolver = |path| { + let resolver2 = |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -1067,8 +1063,12 @@ impl DefCollector<'_> { &path, BuiltinShadowMode::Module, ); - resolved_res.resolved_def.take_macros().map(|it| macro_id_to_def_id(self.db, it)) + resolved_res + .resolved_def + .take_macros() + .map(|it| (it, macro_id_to_def_id(self.db, it))) }; + let resolver = |path| resolver2(path).map(|(_, it)| it); match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { @@ -1087,21 +1087,37 @@ impl DefCollector<'_> { } } MacroDirectiveKind::Derive { ast_id, derive_attr, derive_pos } => { - let call_id = derive_macro_as_call_id( + let id = derive_macro_as_call_id( self.db, ast_id, *derive_attr, *derive_pos as u32, self.def_map.krate, - &resolver, + &resolver2, ); - if let Ok(call_id) = call_id { + + if let Ok((macro_id, def_id, call_id)) = id { self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc( ast_id.ast_id, call_id, *derive_attr, *derive_pos, ); + // Record its helper attributes. + if def_id.krate != self.def_map.krate { + let def_map = self.db.crate_def_map(def_id.krate); + if let Some(helpers) = def_map.exported_derives.get(&def_id) { + self.def_map + .derive_helpers_in_scope + .entry(ast_id.ast_id.map(|it| it.upcast())) + .or_default() + .extend(izip!( + helpers.iter().cloned(), + iter::repeat(macro_id), + iter::repeat(call_id), + )); + } + } push_resolved(directive, call_id); res = ReachedFixedPoint::No; @@ -1132,8 +1148,8 @@ impl DefCollector<'_> { }; if let Some(ident) = path.as_ident() { - if let Some(helpers) = self.derive_helpers_in_scope.get(&ast_id) { - if helpers.contains(ident) { + if let Some(helpers) = self.def_map.derive_helpers_in_scope.get(&ast_id) { + if helpers.iter().any(|(it, ..)| it == ident) { cov_mark::hit!(resolved_derive_helper); // Resolved to derive helper. Collect the item's attributes again, // starting after the derive helper. @@ -1148,7 +1164,7 @@ impl DefCollector<'_> { }; if matches!( def, - MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. } + MacroDefId { kind:MacroDefKind::BuiltInAttr(expander, _),.. } if expander.is_derive() ) { // Resolved to `#[derive]` @@ -1317,19 +1333,6 @@ impl DefCollector<'_> { self.def_map.diagnostics.push(diag); } - // If we've just resolved a derive, record its helper attributes. - if let MacroCallKind::Derive { ast_id, .. } = &loc.kind { - if loc.def.krate != self.def_map.krate { - let def_map = self.db.crate_def_map(loc.def.krate); - if let Some(helpers) = def_map.exported_derives.get(&loc.def) { - self.derive_helpers_in_scope - .entry(ast_id.map(|it| it.upcast())) - .or_default() - .extend(helpers.iter().cloned()); - } - } - } - // Then, fetch and process the item tree. This will reuse the expansion result from above. let item_tree = self.db.file_item_tree(file_id); let mod_dir = self.mod_dirs[&module_id].clone(); @@ -2140,7 +2143,6 @@ mod tests { proc_macros: Default::default(), from_glob_import: Default::default(), skip_attrs: Default::default(), - derive_helpers_in_scope: Default::default(), is_proc_macro: false, }; collector.seed_with_top_level(); diff --git a/crates/hir-def/src/resolver.rs b/crates/hir-def/src/resolver.rs index c8d3052102f4..3163fa0f93fa 100644 --- a/crates/hir-def/src/resolver.rs +++ b/crates/hir-def/src/resolver.rs @@ -149,6 +149,7 @@ impl Resolver { self.resolve_module_path(db, path, BuiltinShadowMode::Module) } + // FIXME: This shouldn't exist pub fn resolve_module_path_in_trait_assoc_items( &self, db: &dyn DefDatabase, @@ -448,10 +449,14 @@ impl Resolver { } pub fn krate(&self) -> CrateId { + self.def_map().krate() + } + + pub fn def_map(&self) -> &DefMap { self.scopes .get(0) .and_then(|scope| match scope { - Scope::ModuleScope(m) => Some(m.def_map.krate()), + Scope::ModuleScope(m) => Some(&m.def_map), _ => None, }) .expect("module scope invariant violated") diff --git a/crates/hir-ty/src/diagnostics/expr.rs b/crates/hir-ty/src/diagnostics/expr.rs index 8cca522aef62..642e03edd230 100644 --- a/crates/hir-ty/src/diagnostics/expr.rs +++ b/crates/hir-ty/src/diagnostics/expr.rs @@ -305,7 +305,10 @@ pub fn record_literal_missing_fields( expr: &Expr, ) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> { let (fields, exhaustive) = match expr { - Expr::RecordLit { path: _, fields, spread } => (fields, spread.is_none()), + Expr::RecordLit { fields, spread, ellipsis, is_assignee_expr, .. } => { + let exhaustive = if *is_assignee_expr { !*ellipsis } else { spread.is_none() }; + (fields, exhaustive) + } _ => return None, }; diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index 2f3346707209..d164e64a8be0 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -276,7 +276,7 @@ impl<'a> InferenceContext<'a> { closure_ty } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { let callee_ty = self.infer_expr(*callee, &Expectation::none()); let mut derefs = Autoderef::new(&mut self.table, callee_ty.clone()); let mut res = None; @@ -421,7 +421,7 @@ impl<'a> InferenceContext<'a> { } TyKind::Never.intern(Interner) } - Expr::RecordLit { path, fields, spread } => { + Expr::RecordLit { path, fields, spread, .. } => { let (ty, def_id) = self.resolve_variant(path.as_deref(), false); if let Some(variant) = def_id { self.write_variant_resolution(tgt_expr.into(), variant); @@ -693,7 +693,7 @@ impl<'a> InferenceContext<'a> { self.err_ty() } } - Expr::Tuple { exprs } => { + Expr::Tuple { exprs, .. } => { let mut tys = match expected .only_has_type(&mut self.table) .as_ref() @@ -724,12 +724,12 @@ impl<'a> InferenceContext<'a> { let expected = Expectation::has_type(elem_ty.clone()); let len = match array { - Array::ElementList(items) => { - for &expr in items.iter() { + Array::ElementList { elements, .. } => { + for &expr in elements.iter() { let cur_elem_ty = self.infer_expr_inner(expr, &expected); coerce.coerce(self, Some(expr), &cur_elem_ty); } - consteval::usize_const(Some(items.len() as u128)) + consteval::usize_const(Some(elements.len() as u128)) } &Array::Repeat { initializer, repeat } => { self.infer_expr_coerce(initializer, &Expectation::has_type(elem_ty)); @@ -850,7 +850,7 @@ impl<'a> InferenceContext<'a> { let rhs_ty = self.resolve_ty_shallow(rhs_ty); let ty = match &self.body[lhs] { - Expr::Tuple { exprs } => { + Expr::Tuple { exprs, .. } => { // We don't consider multiple ellipses. This is analogous to // `hir_def::body::lower::ExprCollector::collect_tuple_pat()`. let ellipsis = exprs.iter().position(|e| is_rest_expr(*e)); @@ -858,7 +858,7 @@ impl<'a> InferenceContext<'a> { self.infer_tuple_pat_like(&rhs_ty, (), ellipsis, &exprs) } - Expr::Call { callee, args } => { + Expr::Call { callee, args, .. } => { // Tuple structs let path = match &self.body[*callee] { Expr::Path(path) => Some(path), @@ -872,7 +872,7 @@ impl<'a> InferenceContext<'a> { self.infer_tuple_struct_pat_like(path, &rhs_ty, (), lhs, ellipsis, &args) } - Expr::Array(Array::ElementList(elements)) => { + Expr::Array(Array::ElementList { elements, .. }) => { let elem_ty = match rhs_ty.kind(Interner) { TyKind::Array(st, _) => st.clone(), _ => self.err_ty(), diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 9ffbb3964cf1..d4925455d7bd 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2252,6 +2252,32 @@ impl Local { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct DeriveHelper { + pub(crate) derive: MacroId, + pub(crate) idx: usize, +} + +impl DeriveHelper { + pub fn derive(&self) -> Macro { + Macro { id: self.derive.into() } + } + + pub fn name(&self, db: &dyn HirDatabase) -> Name { + match self.derive { + MacroId::Macro2Id(_) => None, + MacroId::MacroRulesId(_) => None, + MacroId::ProcMacroId(proc_macro) => db + .proc_macro_data(proc_macro) + .helpers + .as_ref() + .and_then(|it| it.get(self.idx)) + .cloned(), + } + .unwrap_or_else(|| Name::missing()) + } +} + // FIXME: Wrong name? This is could also be a registered attribute #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct BuiltinAttr { diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 043f2b7c24dc..fc8f23f19ab9 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -29,9 +29,9 @@ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, - Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, - HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, - ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, + Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, + HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, + Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -47,6 +47,7 @@ pub enum PathResolution { SelfType(Impl), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), + DeriveHelper(DeriveHelper), } impl PathResolution { @@ -71,6 +72,7 @@ impl PathResolution { PathResolution::BuiltinAttr(_) | PathResolution::ToolModule(_) | PathResolution::Local(_) + | PathResolution::DeriveHelper(_) | PathResolution::ConstParam(_) => None, PathResolution::TypeParam(param) => Some(TypeNs::GenericParam((*param).into())), PathResolution::SelfType(impl_def) => Some(TypeNs::SelfType((*impl_def).into())), @@ -733,6 +735,8 @@ impl<'db> SemanticsImpl<'db> { Some(it) => it, None => return, }; + let def_map = sa.resolver.def_map(); + let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)]; let mut cache = self.expansion_info_cache.borrow_mut(); let mut mcache = self.macro_call_cache.borrow_mut(); @@ -764,7 +768,7 @@ impl<'db> SemanticsImpl<'db> { while let Some(token) = stack.pop() { self.db.unwind_if_cancelled(); let was_not_remapped = (|| { - // are we inside an attribute macro call + // First expand into attribute invocations let containing_attribute_macro_call = self.with_ctx(|ctx| { token.value.parent_ancestors().filter_map(ast::Item::cast).find_map(|item| { if item.attrs().next().is_none() { @@ -784,34 +788,49 @@ impl<'db> SemanticsImpl<'db> { ); } - // or are we inside a function-like macro call - if let Some(tt) = - // FIXME replace map.while_some with take_while once stable - token - .value - .parent_ancestors() - .map(ast::TokenTree::cast) - .while_some() - .last() - { - let parent = tt.syntax().parent()?; - // check for derive attribute here - let macro_call = match_ast! { - match parent { - ast::MacroCall(mcall) => mcall, - // attribute we failed expansion for earlier, this might be a derive invocation + // Then check for token trees, that means we are either in a function-like macro or + // secondary attribute inputs + let tt = token.value.parent_ancestors().map_while(ast::TokenTree::cast).last()?; + let parent = tt.syntax().parent()?; + + if tt.left_delimiter_token().map_or(false, |it| it == token.value) { + return None; + } + if tt.right_delimiter_token().map_or(false, |it| it == token.value) { + return None; + } + + if let Some(macro_call) = ast::MacroCall::cast(parent.clone()) { + let mcall = token.with_value(macro_call); + let file_id = match mcache.get(&mcall) { + Some(&it) => it, + None => { + let it = sa.expand(self.db, mcall.as_ref())?; + mcache.insert(mcall, it); + it + } + }; + process_expansion_for_token(&mut stack, file_id, None, token.as_ref()) + } else if let Some(meta) = ast::Meta::cast(parent.clone()) { + // attribute we failed expansion for earlier, this might be a derive invocation + // or derive helper attribute + let attr = meta.parent_attr()?; + + let adt = if let Some(adt) = attr.syntax().parent().and_then(ast::Adt::cast) { + // this might be a derive, or a derive helper on an ADT + let derive_call = self.with_ctx(|ctx| { // so try downmapping the token into the pseudo derive expansion // see [hir_expand::builtin_attr_macro] for how the pseudo derive expansion works - ast::Meta(meta) => { - let attr = meta.parent_attr()?; - let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; - let call_id = self.with_ctx(|ctx| { - let (_, call_id, _) = ctx.attr_to_derive_macro_call( - token.with_value(&adt), - token.with_value(attr), - )?; - Some(call_id) - })?; + ctx.attr_to_derive_macro_call( + token.with_value(&adt), + token.with_value(attr.clone()), + ) + .map(|(_, call_id, _)| call_id) + }); + + match derive_call { + Some(call_id) => { + // resolved to a derive let file_id = call_id.as_file(); return process_expansion_for_token( &mut stack, @@ -819,32 +838,49 @@ impl<'db> SemanticsImpl<'db> { Some(adt.into()), token.as_ref(), ); - }, - _ => return None, + } + None => Some(adt), } - }; - - if tt.left_delimiter_token().map_or(false, |it| it == token.value) { + } else { + // Otherwise this could be a derive helper on a variant or field + if let Some(field) = attr.syntax().parent().and_then(ast::RecordField::cast) + { + field.syntax().ancestors().take(4).find_map(ast::Adt::cast) + } else if let Some(field) = + attr.syntax().parent().and_then(ast::TupleField::cast) + { + field.syntax().ancestors().take(4).find_map(ast::Adt::cast) + } else if let Some(variant) = + attr.syntax().parent().and_then(ast::Variant::cast) + { + variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast) + } else { + None + } + }?; + if !self.with_ctx(|ctx| ctx.has_derives(InFile::new(token.file_id, &adt))) { return None; } - if tt.right_delimiter_token().map_or(false, |it| it == token.value) { - return None; + // Not an attribute, nor a derive, so it's either a builtin or a derive helper + // Try to resolve to a derive helper and downmap + let attr_name = attr.path().and_then(|it| it.as_single_name_ref())?.as_name(); + let id = self.db.ast_id_map(token.file_id).ast_id(&adt); + let helpers = + def_map.derive_helpers_in_scope(InFile::new(token.file_id, id))?; + let item = Some(adt.into()); + let mut res = None; + for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { + res = res.or(process_expansion_for_token( + &mut stack, + derive.as_file(), + item.clone(), + token.as_ref(), + )); } - - let mcall = token.with_value(macro_call); - let file_id = match mcache.get(&mcall) { - Some(&it) => it, - None => { - let it = sa.expand(self.db, mcall.as_ref())?; - mcache.insert(mcall, it); - it - } - }; - return process_expansion_for_token(&mut stack, file_id, None, token.as_ref()); + res + } else { + None } - - // outside of a macro invocation so this is a "final" token - None })() .is_none(); diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs index 5c4cfa7b45a5..ba9a1cfb6b51 100644 --- a/crates/hir/src/semantics/source_to_def.rs +++ b/crates/hir/src/semantics/source_to_def.rs @@ -247,6 +247,7 @@ impl SourceToDefCtx<'_, '_> { map[keys::ATTR_MACRO_CALL].get(&src.value).copied() } + /// (AttrId, derive attribute call id, derive call ids) pub(super) fn attr_to_derive_macro_call( &mut self, item: InFile<&ast::Adt>, @@ -257,6 +258,7 @@ impl SourceToDefCtx<'_, '_> { .get(&src.value) .map(|&(attr_id, call_id, ref ids)| (attr_id, call_id, &**ids)) } + pub(super) fn has_derives(&mut self, adt: InFile<&ast::Adt>) -> bool { self.dyn_map(adt).as_ref().map_or(false, |map| !map[keys::DERIVE_MACRO_CALL].is_empty()) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index e89f8a542981..1eb51b20c356 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -35,6 +35,7 @@ use hir_ty::{ method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt, TyKind, TyLoweringContext, }; +use itertools::Itertools; use smallvec::SmallVec; use syntax::{ ast::{self, AstNode}, @@ -43,8 +44,8 @@ use syntax::{ use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Callable, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, - ToolModule, Trait, Type, TypeAlias, Variant, + BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, + Struct, ToolModule, Trait, Type, TypeAlias, Variant, }; /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of @@ -429,19 +430,21 @@ impl SourceAnalyzer { } } - let is_path_of_attr = path + let meta_path = path .syntax() .ancestors() - .map(|it| it.kind()) - .take_while(|&kind| ast::Path::can_cast(kind) || ast::Meta::can_cast(kind)) + .take_while(|it| { + let kind = it.kind(); + ast::Path::can_cast(kind) || ast::Meta::can_cast(kind) + }) .last() - .map_or(false, ast::Meta::can_cast); + .and_then(ast::Meta::cast); // Case where path is a qualifier of another path, e.g. foo::bar::Baz where we are // trying to resolve foo::bar. if path.parent_path().is_some() { return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path) { - None if is_path_of_attr => { + None if meta_path.is_some() => { path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) .map(PathResolution::ToolModule) @@ -449,16 +452,56 @@ impl SourceAnalyzer { } res => res, }; - } else if is_path_of_attr { + } else if let Some(meta_path) = meta_path { // Case where we are resolving the final path segment of a path in an attribute // in this case we have to check for inert/builtin attributes and tools and prioritize // resolution of attributes over other namespaces - let name_ref = path.as_single_name_ref(); - let builtin = name_ref.as_ref().and_then(|name_ref| { - BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()) - }); - if let Some(_) = builtin { - return builtin.map(PathResolution::BuiltinAttr); + if let Some(name_ref) = path.as_single_name_ref() { + let builtin = + BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); + if let Some(_) = builtin { + return builtin.map(PathResolution::BuiltinAttr); + } + + if let Some(attr) = meta_path.parent_attr() { + let adt = if let Some(field) = + attr.syntax().parent().and_then(ast::RecordField::cast) + { + field.syntax().ancestors().take(4).find_map(ast::Adt::cast) + } else if let Some(field) = + attr.syntax().parent().and_then(ast::TupleField::cast) + { + field.syntax().ancestors().take(4).find_map(ast::Adt::cast) + } else if let Some(variant) = + attr.syntax().parent().and_then(ast::Variant::cast) + { + variant.syntax().ancestors().nth(2).and_then(ast::Adt::cast) + } else { + None + }; + if let Some(adt) = adt { + let ast_id = db.ast_id_map(self.file_id).ast_id(&adt); + if let Some(helpers) = self + .resolver + .def_map() + .derive_helpers_in_scope(InFile::new(self.file_id, ast_id)) + { + // FIXME: Multiple derives can have the same helper + let name_ref = name_ref.as_name(); + for (macro_id, mut helpers) in + helpers.iter().group_by(|(_, macro_id, ..)| macro_id).into_iter() + { + if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref) + { + return Some(PathResolution::DeriveHelper(DeriveHelper { + derive: *macro_id, + idx, + })); + } + } + } + } + } } return match resolve_hir_path_as_macro(db, &self.resolver, &hir_path) { Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 7f2a26ad0674..c808c010c672 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -145,13 +145,16 @@ fn add_missing_impl_members_inner( Some(cap) => { let mut cursor = Cursor::Before(first_new_item.syntax()); let placeholder; - if let ast::AssocItem::Fn(func) = &first_new_item { - if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() { - if let Some(m) = func.syntax().descendants().find_map(ast::MacroCall::cast) - { - if m.syntax().text() == "todo!()" { - placeholder = m; - cursor = Cursor::Replace(placeholder.syntax()); + if let DefaultMethods::No = mode { + if let ast::AssocItem::Fn(func) = &first_new_item { + if try_gen_trait_body(ctx, func, &trait_, &impl_def).is_none() { + if let Some(m) = + func.syntax().descendants().find_map(ast::MacroCall::cast) + { + if m.syntax().text() == "todo!()" { + placeholder = m; + cursor = Cursor::Replace(placeholder.syntax()); + } } } } diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index ec4835969f88..7a0c912959a1 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -5,7 +5,7 @@ use syntax::{ ted, }; -/// Generate custom trait bodies where possible. +/// Generate custom trait bodies without default implementation where possible. /// /// Returns `Option` so that we can use `?` rather than `if let Some`. Returning /// `None` means that generating a custom trait body failed, and the body will remain @@ -28,6 +28,7 @@ pub(crate) fn gen_trait_fn_body( /// Generate a `Clone` impl based on the fields and members of the target type. fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + stdx::always!(func.name().map_or(false, |name| name.text() == "clone")); fn gen_clone_call(target: ast::Expr) -> ast::Expr { let method = make::name_ref("clone"); make::expr_method_call(target, method, make::arg_list(None)) @@ -339,6 +340,7 @@ fn gen_default_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `Hash` impl based on the fields and members of the target type. fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + stdx::always!(func.name().map_or(false, |name| name.text() == "hash")); fn gen_hash_call(target: ast::Expr) -> ast::Stmt { let method = make::name_ref("hash"); let arg = make::expr_path(make::ext::ident_path("state")); @@ -394,9 +396,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { /// Generate a `PartialEq` impl based on the fields and members of the target type. fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { - if func.name().map_or(false, |name| name.text() == "ne") { - return None; - } + stdx::always!(func.name().map_or(false, |name| name.text() == "eq")); fn gen_eq_chain(expr: Option, cmp: ast::Expr) -> Option { match expr { Some(expr) => Some(make::expr_bin_op(expr, BinaryOp::LogicOp(LogicOp::And), cmp)), @@ -573,6 +573,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { + stdx::always!(func.name().map_or(false, |name| name.text() == "partial_cmp")); fn gen_partial_eq_match(match_target: ast::Expr) -> Option { let mut arms = vec![]; @@ -643,7 +644,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { make::block_expr(stmts.into_iter(), tail).indent(ast::edit::IndentLevel(1)) } - // No fields in the body means there's nothing to hash. + // No fields in the body means there's nothing to compare. None => { let expr = make::expr_literal("true").into(); make::block_expr(None, Some(expr)).indent(ast::edit::IndentLevel(1)) diff --git a/crates/ide-completion/src/item.rs b/crates/ide-completion/src/item.rs index 27482ea489be..27c3ccb35a1e 100644 --- a/crates/ide-completion/src/item.rs +++ b/crates/ide-completion/src/item.rs @@ -292,6 +292,7 @@ impl CompletionItemKind { SymbolKind::Const => "ct", SymbolKind::ConstParam => "cp", SymbolKind::Derive => "de", + SymbolKind::DeriveHelper => "dh", SymbolKind::Enum => "en", SymbolKind::Field => "fd", SymbolKind::Function => "fn", diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 3f25b294e018..9b25964a6086 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -121,7 +121,7 @@ pub(crate) fn render_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - receiver.map_or_else(|| name.clone(), |receiver| format!("{}.{}", receiver, name).into()), + field_with_receiver(receiver.as_ref(), &name), ); item.set_relevance(CompletionRelevance { type_match: compute_type_match(ctx.completion, ty), @@ -132,7 +132,7 @@ pub(crate) fn render_field( .set_documentation(field.docs(ctx.db())) .set_deprecated(is_deprecated) .lookup_by(name.clone()); - item.insert_text(escaped_name); + item.insert_text(field_with_receiver(receiver.as_ref(), &escaped_name)); if let Some(receiver) = &dot_access.receiver { if let Some(original) = ctx.completion.sema.original_ast_node(receiver.clone()) { if let Some(ref_match) = compute_ref_match(ctx.completion, ty) { @@ -143,6 +143,11 @@ pub(crate) fn render_field( item.build() } +fn field_with_receiver(receiver: Option<&hir::Name>, field_name: &str) -> SmolStr { + receiver + .map_or_else(|| field_name.into(), |receiver| format!("{}.{}", receiver, field_name).into()) +} + pub(crate) fn render_tuple_field( ctx: RenderContext<'_>, receiver: Option, @@ -152,7 +157,7 @@ pub(crate) fn render_tuple_field( let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), - receiver.map_or_else(|| field.to_string(), |receiver| format!("{}.{}", receiver, field)), + field_with_receiver(receiver.as_ref(), &field.to_string()), ); item.detail(ty.display(ctx.db()).to_string()).lookup_by(field.to_string()); item.build() @@ -1873,6 +1878,35 @@ impl r#trait for r#struct { type t$0 } struct r#struct {} trait r#trait { type r#type; } impl r#trait for r#struct { type r#type = $0; } +"#, + ) + } + + #[test] + fn field_access_includes_self() { + check_edit( + "length", + r#" +struct S { + length: i32 +} + +impl S { + fn some_fn(&self) { + let l = len$0 + } +} +"#, + r#" +struct S { + length: i32 +} + +impl S { + fn some_fn(&self) { + let l = self.length + } +} "#, ) } diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index 692a31572030..aeaca00ec65c 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -7,9 +7,9 @@ use arrayvec::ArrayVec; use hir::{ - Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, Field, Function, - GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, Name, - PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, + Adt, AsAssocItem, AssocItem, BuiltinAttr, BuiltinType, Const, Crate, DeriveHelper, Field, + Function, GenericParam, HasVisibility, Impl, ItemInNs, Label, Local, Macro, Module, ModuleDef, + Name, PathResolution, Semantics, Static, ToolModule, Trait, TypeAlias, Variant, Visibility, }; use stdx::impl_from; use syntax::{ @@ -37,6 +37,7 @@ pub enum Definition { Local(Local), GenericParam(GenericParam), Label(Label), + DeriveHelper(DeriveHelper), BuiltinAttr(BuiltinAttr), ToolModule(ToolModule), } @@ -69,6 +70,7 @@ impl Definition { Definition::Local(it) => it.module(db), Definition::GenericParam(it) => it.module(db), Definition::Label(it) => it.module(db), + Definition::DeriveHelper(it) => it.derive().module(db), Definition::BuiltinAttr(_) | Definition::BuiltinType(_) | Definition::ToolModule(_) => { return None } @@ -94,7 +96,8 @@ impl Definition { | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) - | Definition::Label(_) => return None, + | Definition::Label(_) + | Definition::DeriveHelper(_) => return None, }; Some(vis) } @@ -118,6 +121,7 @@ impl Definition { Definition::Label(it) => it.name(db), Definition::BuiltinAttr(_) => return None, // FIXME Definition::ToolModule(_) => return None, // FIXME + Definition::DeriveHelper(it) => it.name(db), }; Some(name) } @@ -500,6 +504,7 @@ impl From for Definition { PathResolution::SelfType(impl_def) => Definition::SelfType(impl_def), PathResolution::BuiltinAttr(attr) => Definition::BuiltinAttr(attr), PathResolution::ToolModule(tool) => Definition::ToolModule(tool), + PathResolution::DeriveHelper(helper) => Definition::DeriveHelper(helper), } } } diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs index 26648b4d7638..966bba616f62 100644 --- a/crates/ide-db/src/lib.rs +++ b/crates/ide-db/src/lib.rs @@ -173,6 +173,7 @@ pub enum SymbolKind { Const, ConstParam, Derive, + DeriveHelper, Enum, Field, Function, diff --git a/crates/ide-db/src/path_transform.rs b/crates/ide-db/src/path_transform.rs index d78b8758d651..40af9e6fe2ad 100644 --- a/crates/ide-db/src/path_transform.rs +++ b/crates/ide-db/src/path_transform.rs @@ -224,7 +224,8 @@ impl<'a> Ctx<'a> { | hir::PathResolution::SelfType(_) | hir::PathResolution::Def(_) | hir::PathResolution::BuiltinAttr(_) - | hir::PathResolution::ToolModule(_) => (), + | hir::PathResolution::ToolModule(_) + | hir::PathResolution::DeriveHelper(_) => (), } Some(()) } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index bb466e43e752..517fe3f246d0 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -156,6 +156,8 @@ impl Definition { Definition::SelfType(_) => return None, Definition::BuiltinAttr(_) => return None, Definition::ToolModule(_) => return None, + // FIXME: This should be doable in theory + Definition::DeriveHelper(_) => return None, }; return res; @@ -316,14 +318,20 @@ pub fn source_edit_from_references( // macros can cause multiple refs to occur for the same text range, so keep track of what we have edited so far let mut edited_ranges = Vec::new(); for &FileReference { range, ref name, .. } in references { + let name_range = name.syntax().text_range(); + if name_range.len() != range.len() { + // This usage comes from a different token kind that was downmapped to a NameLike in a macro + // Renaming this will most likely break things syntax-wise + continue; + } let has_emitted_edit = match name { // if the ranges differ then the node is inside a macro call, we can't really attempt // to make special rewrites like shorthand syntax and such, so just rename the node in // the macro input - ast::NameLike::NameRef(name_ref) if name_ref.syntax().text_range() == range => { + ast::NameLike::NameRef(name_ref) if name_range == range => { source_edit_from_name_ref(&mut edit, name_ref, new_name, def) } - ast::NameLike::Name(name) if name.syntax().text_range() == range => { + ast::NameLike::Name(name) if name_range == range => { source_edit_from_name(&mut edit, name, new_name) } _ => false, diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index c75364084e36..bd038cdaa068 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -54,7 +54,9 @@ impl IntoIterator for UsageSearchResult { #[derive(Debug, Clone)] pub struct FileReference { + /// The range of the reference in the original file pub range: TextRange, + /// The node of the reference in the (macro-)file pub name: ast::NameLike, pub category: Option, } @@ -276,16 +278,16 @@ impl Definition { } } hir::MacroKind::BuiltIn => SearchScope::crate_graph(db), - // FIXME: We don't actually see derives in derive attributes as these do not - // expand to something that references the derive macro in the output. - // We could get around this by doing pseudo expansions for proc_macro_derive like we - // do for the derive attribute hir::MacroKind::Derive | hir::MacroKind::Attr | hir::MacroKind::ProcMacro => { SearchScope::reverse_dependencies(db, module.krate()) } }; } + if let Definition::DeriveHelper(_) = self { + return SearchScope::reverse_dependencies(db, module.krate()); + } + let vis = self.visibility(db); if let Some(Visibility::Public) = vis { return SearchScope::reverse_dependencies(db, module.krate()); diff --git a/crates/ide-diagnostics/src/handlers/missing_fields.rs b/crates/ide-diagnostics/src/handlers/missing_fields.rs index 30f903af50d5..edb1fc0919c2 100644 --- a/crates/ide-diagnostics/src/handlers/missing_fields.rs +++ b/crates/ide-diagnostics/src/handlers/missing_fields.rs @@ -292,6 +292,37 @@ fn x(a: S) { ) } + #[test] + fn missing_record_expr_in_assignee_expr() { + check_diagnostics( + r" +struct S { s: usize, t: usize } +struct S2 { s: S, t: () } +struct T(S); +fn regular(a: S) { + let s; + S { s, .. } = a; +} +fn nested(a: S2) { + let s; + S2 { s: S { s, .. }, .. } = a; +} +fn in_tuple(a: (S,)) { + let s; + (S { s, .. },) = a; +} +fn in_array(a: [S;1]) { + let s; + [S { s, .. },] = a; +} +fn in_tuple_struct(a: T) { + let s; + T(S { s, .. }) = a; +} + ", + ); + } + #[test] fn range_mapping_out_of_macros() { check_fix( diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs index fed327f52be5..582e9fe7e808 100644 --- a/crates/ide/src/doc_links.rs +++ b/crates/ide/src/doc_links.rs @@ -190,7 +190,8 @@ pub(crate) fn resolve_doc_path_for_def( | Definition::SelfType(_) | Definition::Local(_) | Definition::GenericParam(_) - | Definition::Label(_) => None, + | Definition::Label(_) + | Definition::DeriveHelper(_) => None, } .map(Definition::from) } @@ -515,7 +516,8 @@ fn filename_and_frag_for_def( | Definition::GenericParam(_) | Definition::Label(_) | Definition::BuiltinAttr(_) - | Definition::ToolModule(_) => return None, + | Definition::ToolModule(_) + | Definition::DeriveHelper(_) => return None, }; Some((def, res, None)) diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index d8867cf783ad..59c97f2dcf96 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -115,7 +115,12 @@ pub(crate) fn hover( }); } - let descended = sema.descend_into_macros_with_same_text(original_token.clone()); + let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + let descended = if in_attr { + [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() + } else { + sema.descend_into_macros_with_same_text(original_token.clone()) + }; // FIXME: Definition should include known lints and the like instead of having this special case here let hovered_lint = descended.iter().find_map(|token| { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 925aaa61cdd0..6c50a4e6adc0 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -370,6 +370,7 @@ pub(super) fn definition( // FIXME: We should be able to show more info about these Definition::BuiltinAttr(it) => return render_builtin_attr(db, it), Definition::ToolModule(it) => return Some(Markup::fenced_block(&it.name(db))), + Definition::DeriveHelper(it) => (format!("derive_helper {}", it.name(db)), None), }; let docs = match config.documentation { diff --git a/crates/ide/src/navigation_target.rs b/crates/ide/src/navigation_target.rs index 1fb3b5ec3f38..9f049e298ad1 100644 --- a/crates/ide/src/navigation_target.rs +++ b/crates/ide/src/navigation_target.rs @@ -196,6 +196,8 @@ impl TryToNav for Definition { Definition::BuiltinType(_) => None, Definition::ToolModule(_) => None, Definition::BuiltinAttr(_) => None, + // FIXME: The focus range should be set to the helper declaration + Definition::DeriveHelper(it) => it.derive().try_to_nav(db), } } } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index ba287d13aec1..fedc1a435827 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -237,7 +237,8 @@ fn signature_help_for_generics( | hir::PathResolution::Local(_) | hir::PathResolution::TypeParam(_) | hir::PathResolution::ConstParam(_) - | hir::PathResolution::SelfType(_) => return None, + | hir::PathResolution::SelfType(_) + | hir::PathResolution::DeriveHelper(_) => return None, }; generic_def diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d7ad6a757995..d013d6f4b19f 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -107,6 +107,7 @@ pub struct HlRange { // builtinType:: Emitted for builtin types like `u32`, `str` and `f32`. // comment:: Emitted for comments. // constParameter:: Emitted for const parameters. +// deriveHelper:: Emitted for derive helper attributes. // enumMember:: Emitted for enum variants. // generic:: Emitted for generic tokens that have no mapping. // keyword:: Emitted for keywords. @@ -431,6 +432,13 @@ fn traverse( // let the editor do its highlighting for these tokens instead continue; } + if highlight.tag == HlTag::UnresolvedReference + && matches!(attr_or_derive_item, Some(AttrOrDerive::Derive(_)) if inside_attribute) + { + // do not emit unresolved references in derive helpers if the token mapping maps to + // something unresolvable. FIXME: There should be a way to prevent that + continue; + } if inside_attribute { highlight |= HlMod::Attribute } diff --git a/crates/ide/src/syntax_highlighting/highlight.rs b/crates/ide/src/syntax_highlighting/highlight.rs index fd3723ed4548..9395e914c43a 100644 --- a/crates/ide/src/syntax_highlighting/highlight.rs +++ b/crates/ide/src/syntax_highlighting/highlight.rs @@ -472,6 +472,7 @@ fn highlight_def( Definition::Label(_) => Highlight::new(HlTag::Symbol(SymbolKind::Label)), Definition::BuiltinAttr(_) => Highlight::new(HlTag::Symbol(SymbolKind::BuiltinAttr)), Definition::ToolModule(_) => Highlight::new(HlTag::Symbol(SymbolKind::ToolModule)), + Definition::DeriveHelper(_) => Highlight::new(HlTag::Symbol(SymbolKind::DeriveHelper)), }; 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 94d573a30b04..f779a985a99a 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -270,6 +270,7 @@ fn module_def_to_hl_tag(def: Definition) -> HlTag { Definition::Label(_) => SymbolKind::Label, Definition::BuiltinAttr(_) => SymbolKind::BuiltinAttr, Definition::ToolModule(_) => SymbolKind::ToolModule, + Definition::DeriveHelper(_) => SymbolKind::DeriveHelper, }; HlTag::Symbol(symbol) } diff --git a/crates/ide/src/syntax_highlighting/tags.rs b/crates/ide/src/syntax_highlighting/tags.rs index b900dadcfa3c..5262770f3031 100644 --- a/crates/ide/src/syntax_highlighting/tags.rs +++ b/crates/ide/src/syntax_highlighting/tags.rs @@ -134,6 +134,7 @@ impl HlTag { SymbolKind::Const => "constant", SymbolKind::ConstParam => "const_param", SymbolKind::Derive => "derive", + SymbolKind::DeriveHelper => "derive_helper", SymbolKind::Enum => "enum", SymbolKind::Field => "field", SymbolKind::Function => "function", diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs index ff4c59447d87..c4018d3b39e7 100644 --- a/crates/proc-macro-api/src/process.rs +++ b/crates/proc-macro-api/src/process.rs @@ -86,6 +86,7 @@ fn mk_child( ) -> io::Result { Command::new(path.as_os_str()) .args(args) + .env("RUST_ANALYZER_INTERNALS_DO_NOT_USE", "this is unstable") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::inherit()) diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml new file mode 100644 index 000000000000..9d0da5dee9c1 --- /dev/null +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "proc-macro-srv-cli" +version = "0.0.0" +description = "TBD" +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.57" + +[dependencies] +proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } + +[features] +sysroot-abi = ["proc-macro-srv/sysroot-abi"] + +[[bin]] +name = "rust-analyzer-proc-macro-srv" +path = "src/main.rs" diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs new file mode 100644 index 000000000000..ac9fa9f5a4ce --- /dev/null +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -0,0 +1,19 @@ +//! A standalone binary for `proc-macro-srv`. + +use proc_macro_srv::cli; + +fn main() -> std::io::Result<()> { + let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); + match v.as_deref() { + Ok("this is unstable") => { + // very well, if you must + } + _ => { + eprintln!("If you're rust-analyzer, you can use this tool by exporting RUST_ANALYZER_INTERNALS_DO_NOT_USE='this is unstable'."); + eprintln!("If not, you probably shouldn't use this tool. But do what you want: I'm an error message, not a cop."); + std::process::exit(122); + } + } + + cli::run() +} diff --git a/crates/proc-macro-test/build.rs b/crates/proc-macro-test/build.rs index c90144509dec..a80c962617bb 100644 --- a/crates/proc-macro-test/build.rs +++ b/crates/proc-macro-test/build.rs @@ -62,8 +62,7 @@ fn main() { Command::new(toolchain::cargo()) }; - cmd - .current_dir(&staging_dir) + cmd.current_dir(&staging_dir) .args(&["build", "-p", "proc-macro-test-impl", "--message-format", "json"]) // Explicit override the target directory to avoid using the same one which the parent // cargo is using, or we'll deadlock. diff --git a/crates/project-model/src/project_json.rs b/crates/project-model/src/project_json.rs index a3c5ac167406..63d1d0ace96b 100644 --- a/crates/project-model/src/project_json.rs +++ b/crates/project-model/src/project_json.rs @@ -17,6 +17,9 @@ use crate::cfg_flag::CfgFlag; /// Roots and crates that compose this Rust project. #[derive(Clone, Debug, Eq, PartialEq)] pub struct ProjectJson { + /// e.g. `path/to/sysroot` + pub(crate) sysroot: Option, + /// e.g. `path/to/sysroot/lib/rustlib/src/rust` pub(crate) sysroot_src: Option, project_root: AbsPathBuf, crates: Vec, @@ -52,6 +55,7 @@ impl ProjectJson { /// configuration. pub fn new(base: &AbsPath, data: ProjectJsonData) -> ProjectJson { ProjectJson { + sysroot: data.sysroot.map(|it| base.join(it)), sysroot_src: data.sysroot_src.map(|it| base.join(it)), project_root: base.to_path_buf(), crates: data @@ -122,6 +126,7 @@ impl ProjectJson { #[derive(Deserialize, Debug, Clone)] pub struct ProjectJsonData { + sysroot: Option, sysroot_src: Option, crates: Vec, } diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 52750f48969f..362bb0f5e79c 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -15,6 +15,7 @@ use crate::{utf8_stdout, ManifestPath}; #[derive(Debug, Clone, Eq, PartialEq)] pub struct Sysroot { root: AbsPathBuf, + src_root: AbsPathBuf, crates: Arena, } @@ -35,10 +36,19 @@ impl ops::Index for Sysroot { } impl Sysroot { + /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/` + /// subfolder live, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu` pub fn root(&self) -> &AbsPath { &self.root } + /// Returns the sysroot "source" directory, where stdlib sources are located, like: + /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library` + pub fn src_root(&self) -> &AbsPath { + &self.src_root + } + pub fn public_deps(&self) -> impl Iterator + '_ { // core is added as a dependency before std in order to // mimic rustcs dependency order @@ -61,7 +71,7 @@ impl Sysroot { tracing::debug!("Discovering sysroot for {}", dir.display()); let sysroot_dir = discover_sysroot_dir(dir)?; let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, dir)?; - let res = Sysroot::load(sysroot_src_dir)?; + let res = Sysroot::load(sysroot_dir, sysroot_src_dir)?; Ok(res) } @@ -71,14 +81,15 @@ impl Sysroot { discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir)) } - pub fn load(sysroot_src_dir: AbsPathBuf) -> Result { - let mut sysroot = Sysroot { root: sysroot_src_dir, crates: Arena::default() }; + pub fn load(sysroot_dir: AbsPathBuf, sysroot_src_dir: AbsPathBuf) -> Result { + let mut sysroot = + Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }; for path in SYSROOT_CRATES.trim().lines() { let name = path.split('/').last().unwrap(); let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)] .into_iter() - .map(|it| sysroot.root.join(it)) + .map(|it| sysroot.src_root.join(it)) .filter_map(|it| ManifestPath::try_from(it).ok()) .find(|it| fs::metadata(it).is_ok()); @@ -119,7 +130,7 @@ impl Sysroot { }; anyhow::bail!( "could not find libcore in sysroot path `{}`{}", - sysroot.root.as_path().display(), + sysroot.src_root.as_path().display(), var_note, ); } diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index ddfea0ce4c4f..e304a59c0180 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -75,8 +75,11 @@ fn get_test_path(file: &str) -> PathBuf { fn get_fake_sysroot() -> Sysroot { let sysroot_path = get_test_path("fake-sysroot"); - let sysroot_src_dir = AbsPathBuf::assert(sysroot_path); - Sysroot::load(sysroot_src_dir).unwrap() + // 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(sysroot_path); + let sysroot_src_dir = sysroot_dir.clone(); + Sysroot::load(sysroot_dir, sysroot_src_dir).unwrap() } fn rooted_project_json(data: ProjectJsonData) -> ProjectJson { diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index de4245835456..b144006b44e0 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -230,10 +230,26 @@ impl ProjectWorkspace { project_json: ProjectJson, target: Option<&str>, ) -> Result { - let sysroot = match &project_json.sysroot_src { - Some(path) => Some(Sysroot::load(path.clone())?), - None => None, + let sysroot = match (project_json.sysroot.clone(), project_json.sysroot_src.clone()) { + (Some(sysroot), Some(sysroot_src)) => Some(Sysroot::load(sysroot, sysroot_src)?), + (Some(sysroot), None) => { + // assume sysroot is structured like rustup's and guess `sysroot_src` + let sysroot_src = + sysroot.join("lib").join("rustlib").join("src").join("rust").join("library"); + + Some(Sysroot::load(sysroot, sysroot_src)?) + } + (None, Some(sysroot_src)) => { + // assume sysroot is structured like rustup's and guess `sysroot` + let mut sysroot = sysroot_src.clone(); + for _ in 0..5 { + sysroot.pop(); + } + Some(Sysroot::load(sysroot, sysroot_src)?) + } + (None, None) => None, }; + let rustc_cfg = rustc_cfg::get(None, target); Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) } @@ -345,7 +361,7 @@ impl ProjectWorkspace { }) .chain(sysroot.iter().map(|sysroot| PackageRoot { is_local: false, - include: vec![sysroot.root().to_path_buf()], + include: vec![sysroot.src_root().to_path_buf()], exclude: Vec::new(), })) .chain(rustc.iter().flat_map(|rustc| { diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 4243af25ff1a..0ada4b73e842 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -60,9 +60,9 @@ pub fn load_workspace( let proc_macro_client = if load_config.with_proc_macro { let path = AbsPathBuf::assert(std::env::current_exe()?); - Some(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap()) + Ok(ProcMacroServer::spawn(path, &["proc-macro"]).unwrap()) } else { - None + Err("proc macro server not started".to_owned()) }; let crate_graph = ws.to_crate_graph( @@ -89,7 +89,7 @@ pub fn load_workspace( if load_config.prefill_caches { host.analysis().parallel_prime_caches(1, |_| {})?; } - Ok((host, vfs, proc_macro_client)) + Ok((host, vfs, proc_macro_client.ok())) } fn load_crate_graph( diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 4af60035a20e..8f881cba4dbd 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -61,7 +61,7 @@ pub(crate) struct GlobalState { pub(crate) proc_macro_changed: bool, pub(crate) last_reported_status: Option, pub(crate) source_root_config: SourceRootConfig, - pub(crate) proc_macro_client: Option, + pub(crate) proc_macro_clients: Vec>, pub(crate) flycheck: Vec, pub(crate) flycheck_sender: Sender, @@ -151,7 +151,7 @@ impl GlobalState { proc_macro_changed: false, last_reported_status: None, source_root_config: SourceRootConfig::default(), - proc_macro_client: None, + proc_macro_clients: vec![], flycheck: Vec::new(), flycheck_sender, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 520aa7d1dd48..deb777c952fd 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -44,7 +44,7 @@ use crate::{ }; pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<()> { - state.proc_macro_client = None; + state.proc_macro_clients.clear(); state.proc_macro_changed = false; state.fetch_workspaces_queue.request_op("reload workspace request".to_string()); state.fetch_build_data_queue.request_op("reload workspace request".to_string()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 77125f88f473..9ae361b034e2 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -303,18 +303,55 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); - if self.proc_macro_client.is_none() { + if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { - match ProcMacroServer::spawn(path.clone(), args) { - Ok(it) => self.proc_macro_client = Some(it), - Err(err) => { - tracing::error!( - "Failed to run proc_macro_srv from path {}, error: {:?}", + self.proc_macro_clients = self + .workspaces + .iter() + .map(|ws| { + let mut args = args.clone(); + let mut path = path.clone(); + + if let ProjectWorkspace::Cargo { sysroot, .. } = ws { + tracing::info!("Found a cargo workspace..."); + if let Some(sysroot) = sysroot.as_ref() { + tracing::info!("Found a cargo workspace with a sysroot..."); + let server_path = sysroot + .root() + .join("libexec") + .join("rust-analyzer-proc-macro-srv"); + if std::fs::metadata(&server_path).is_ok() { + tracing::info!( + "And the server exists at {}", + server_path.display() + ); + path = server_path; + args = vec![]; + } else { + tracing::info!( + "And the server does not exist at {}", + server_path.display() + ); + } + } + } + + tracing::info!( + "Using proc-macro server at {} with args {:?}", path.display(), - err + args ); - } - } + ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| { + let error = format!( + "Failed to run proc_macro_srv from path {}, error: {:?}", + path.display(), + err + ); + tracing::error!(error); + error + }) + }) + .collect(); } } @@ -331,15 +368,7 @@ impl GlobalState { // Create crate graph from all the workspaces let crate_graph = { - let proc_macro_client = self.proc_macro_client.as_ref(); let dummy_replacements = self.config.dummy_replacements(); - let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { - load_proc_macro( - proc_macro_client, - path, - dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), - ) - }; let vfs = &mut self.vfs.write().0; let loader = &mut self.loader; @@ -359,7 +388,15 @@ impl GlobalState { }; let mut crate_graph = CrateGraph::default(); - for ws in self.workspaces.iter() { + for (idx, ws) in self.workspaces.iter().enumerate() { + let proc_macro_client = self.proc_macro_clients[idx].as_ref(); + let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { + load_proc_macro( + proc_macro_client, + path, + dummy_replacements.get(crate_name).map(|v| &**v).unwrap_or_default(), + ) + }; crate_graph.extend(ws.to_crate_graph(&mut load_proc_macro, &mut load)); } crate_graph @@ -536,14 +573,14 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` /// with an identity dummy expander. pub(crate) fn load_proc_macro( - server: Option<&ProcMacroServer>, + server: Result<&ProcMacroServer, &String>, path: &AbsPath, dummy_replace: &[Box], ) -> ProcMacroLoadResult { let res: Result, String> = (|| { let dylib = MacroDylib::new(path.to_path_buf()) .map_err(|io| format!("Proc-macro dylib loading failed: {io}"))?; - let server = server.ok_or_else(|| format!("Proc-macro server not started"))?; + let server = server.map_err(ToOwned::to_owned)?; let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?; if vec.is_empty() { return Err("proc macro library returned no proc macros".to_string()); diff --git a/crates/rust-analyzer/src/semantic_tokens.rs b/crates/rust-analyzer/src/semantic_tokens.rs index 5fb945ea9872..6c78b5df1a70 100644 --- a/crates/rust-analyzer/src/semantic_tokens.rs +++ b/crates/rust-analyzer/src/semantic_tokens.rs @@ -54,6 +54,7 @@ define_semantic_token_types![ (COMPARISON, "comparison"), (CONST_PARAMETER, "constParameter"), (DERIVE, "derive"), + (DERIVE_HELPER, "deriveHelper"), (DOT, "dot"), (ESCAPE_SEQUENCE, "escapeSequence"), (FORMAT_SPECIFIER, "formatSpecifier"), diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 9c8e618b6ed0..7f4fa57fa1e0 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -53,7 +53,8 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind { SymbolKind::Macro | SymbolKind::BuiltinAttr | SymbolKind::Attribute - | SymbolKind::Derive => lsp_types::SymbolKind::FUNCTION, + | SymbolKind::Derive + | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION, SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE, SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => { lsp_types::SymbolKind::TYPE_PARAMETER @@ -117,6 +118,7 @@ pub(crate) fn completion_item_kind( SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT, SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER, SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION, SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM, SymbolKind::Field => lsp_types::CompletionItemKind::FIELD, SymbolKind::Function => lsp_types::CompletionItemKind::FUNCTION, @@ -561,6 +563,7 @@ fn semantic_token_type_and_modifiers( HlTag::Symbol(symbol) => match symbol { SymbolKind::Attribute => semantic_tokens::ATTRIBUTE, SymbolKind::Derive => semantic_tokens::DERIVE, + SymbolKind::DeriveHelper => semantic_tokens::DERIVE_HELPER, SymbolKind::Module => lsp_types::SemanticTokenType::NAMESPACE, SymbolKind::Impl => semantic_tokens::TYPE_ALIAS, SymbolKind::Field => lsp_types::SemanticTokenType::PROPERTY, From 9d2cb42a413e51deb50b36794a2e1605381878fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 2 Aug 2022 09:05:16 +0300 Subject: [PATCH 02/22] :arrow_up: rust-analyzer --- .github/workflows/publish.yml | 17 +- crates/hir-def/src/attr.rs | 21 +- crates/hir-def/src/item_scope.rs | 10 +- crates/hir-def/src/nameres/collector.rs | 13 +- crates/hir-ty/src/chalk_ext.rs | 5 + crates/hir/src/lib.rs | 4 + crates/hir/src/semantics.rs | 25 +- .../src/handlers/expand_glob_import.rs | 10 +- .../ide-assists/src/handlers/inline_call.rs | 48 ++- crates/ide-assists/src/tests/generated.rs | 2 +- crates/ide-completion/src/completions.rs | 13 +- .../src/completions/attribute.rs | 2 +- .../src/completions/attribute/derive.rs | 2 +- crates/ide-completion/src/completions/expr.rs | 56 +++- .../src/completions/item_list.rs | 2 +- .../ide-completion/src/completions/pattern.rs | 4 +- crates/ide-completion/src/completions/type.rs | 18 +- crates/ide-completion/src/completions/use_.rs | 2 +- crates/ide-completion/src/completions/vis.rs | 2 +- crates/ide-completion/src/context.rs | 5 +- crates/ide-completion/src/context/analysis.rs | 314 +++++++++++------- crates/ide-completion/src/context/tests.rs | 20 ++ crates/ide-completion/src/render.rs | 25 +- crates/ide-completion/src/render/function.rs | 4 +- crates/ide-completion/src/render/pattern.rs | 23 +- crates/ide-completion/src/tests.rs | 7 +- crates/ide-completion/src/tests/expression.rs | 21 +- crates/ide-completion/src/tests/pattern.rs | 4 +- crates/ide-completion/src/tests/record.rs | 1 - crates/ide-completion/src/tests/special.rs | 55 ++- crates/ide/src/syntax_highlighting.rs | 4 +- crates/ide/src/syntax_highlighting/inject.rs | 9 +- .../highlight_module_docs_inline.html | 51 +++ .../highlight_module_docs_outline.html | 50 +++ crates/ide/src/syntax_highlighting/tests.rs | 46 +++ crates/limit/src/lib.rs | 15 +- crates/parser/src/grammar/paths.rs | 2 +- crates/proc-macro-srv/src/lib.rs | 13 +- crates/rust-analyzer/src/cli/load_cargo.rs | 4 +- crates/rust-analyzer/src/reload.rs | 16 +- crates/syntax/src/ast/generated/nodes.rs | 4 - crates/syntax/src/ast/node_ext.rs | 38 +++ crates/syntax/src/tests/sourcegen_ast.rs | 2 + docs/dev/README.md | 7 +- xtask/src/release.rs | 10 +- 45 files changed, 765 insertions(+), 241 deletions(-) create mode 100644 crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html create mode 100644 crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 927996c1bef2..a4497f49e3c2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -34,8 +34,21 @@ jobs: git config --global user.email "runner@gha.local" git config --global user.name "Github Action" rm Cargo.lock + # Fix names for crates that were published before switch to kebab-case. + cargo workspaces rename --from base-db base_db + cargo workspaces rename --from hir-def hir_def + cargo workspaces rename --from hir-expand hir_expand + cargo workspaces rename --from hir-ty hir_ty + cargo workspaces rename --from ide-assists ide_assists + cargo workspaces rename --from ide-completion ide_completion + cargo workspaces rename --from ide-db ide_db + cargo workspaces rename --from ide-diagnostics ide_diagnostics + cargo workspaces rename --from ide-ssr ide_ssr + cargo workspaces rename --from proc-macro-api proc_macro_api + cargo workspaces rename --from proc-macro-srv proc_macro_srv + cargo workspaces rename --from project-model project_model + cargo workspaces rename --from test-utils test_utils + cargo workspaces rename --from text-edit text_edit cargo workspaces rename ra_ap_%n find crates/rust-analyzer -type f -name '*.rs' -exec sed -i 's/rust_analyzer/ra_ap_rust_analyzer/g' {} + - # Fix names for crates that were published before switch to kebab-case. - find crates -name 'Cargo.toml' -exec sed -i "s/ra_ap_base-db/ra_ap_base_db/g; s/ra_ap_hir-def/ra_ap_hir_def/g; s/ra_ap_hir-expand/ra_ap_hir_expand/g; s/ra_ap_hir-ty/ra_ap_hir_ty/g; s/ra_ap_ide-assists/ra_ap_ide_assists/g; s/ra_ap_ide-completion/ra_ap_ide_completion/g; s/ra_ap_ide-db/ra_ap_ide_db/g; s/ra_ap_ide-diagnostics/ra_ap_ide_diagnostics/g; s/ra_ap_ide-ssr/ra_ap_ide_ssr/g; s/ra_ap_proc-macro-api/ra_ap_proc_macro_api/g; s/ra_ap_proc-macro-srv/ra_ap_proc_macro_srv/g; s/ra_ap_project-model/ra_ap_project_model/g; s/ra_ap_test-utils/ra_ap_test_utils/g; s/ra_ap_text-edit/ra_ap_text_edit/g" {} + cargo workspaces publish --yes --force '*' --exact --no-git-commit --allow-dirty --skip-published custom 0.0.$PATCH diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs index 8a6b6f3effd2..2b39c6f8da86 100644 --- a/crates/hir-def/src/attr.rs +++ b/crates/hir-def/src/attr.rs @@ -124,13 +124,24 @@ impl RawAttrs { pub(crate) fn merge(&self, other: Self) -> Self { // FIXME: This needs to fixup `AttrId`s - match (&self.entries, &other.entries) { + match (&self.entries, other.entries) { (None, None) => Self::EMPTY, - (Some(entries), None) | (None, Some(entries)) => { - Self { entries: Some(entries.clone()) } - } + (None, entries @ Some(_)) => Self { entries }, + (Some(entries), None) => Self { entries: Some(entries.clone()) }, (Some(a), Some(b)) => { - Self { entries: Some(a.iter().chain(b.iter()).cloned().collect()) } + let last_ast_index = a.last().map_or(0, |it| it.id.ast_index + 1); + Self { + entries: Some( + a.iter() + .cloned() + .chain(b.iter().map(|it| { + let mut it = it.clone(); + it.id.ast_index += last_ast_index; + it + })) + .collect(), + ), + } } } } diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 579f803ea193..a11a92204c15 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -5,6 +5,7 @@ use std::collections::hash_map::Entry; use base_db::CrateId; use hir_expand::{name::Name, AstId, MacroCallId}; +use itertools::Itertools; use once_cell::sync::Lazy; use profile::Count; use rustc_hash::{FxHashMap, FxHashSet}; @@ -97,15 +98,14 @@ pub(crate) enum BuiltinShadowMode { impl ItemScope { pub fn entries<'a>(&'a self) -> impl Iterator + 'a { // FIXME: shadowing - let keys: FxHashSet<_> = self - .types + self.types .keys() .chain(self.values.keys()) .chain(self.macros.keys()) .chain(self.unresolved.iter()) - .collect(); - - keys.into_iter().map(move |name| (name, self.get(name))) + .sorted() + .unique() + .map(move |name| (name, self.get(name))) } pub fn declarations(&self) -> impl Iterator + '_ { diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index f394c541719f..8a6bb929c3df 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -1055,7 +1055,7 @@ impl DefCollector<'_> { }; let mut res = ReachedFixedPoint::Yes; macros.retain(|directive| { - let resolver2 = |path| { + let resolver = |path| { let resolved_res = self.def_map.resolve_path_fp_with_macro( self.db, ResolveMode::Other, @@ -1068,7 +1068,7 @@ impl DefCollector<'_> { .take_macros() .map(|it| (it, macro_id_to_def_id(self.db, it))) }; - let resolver = |path| resolver2(path).map(|(_, it)| it); + let resolver_def_id = |path| resolver(path).map(|(_, it)| it); match &directive.kind { MacroDirectiveKind::FnLike { ast_id, expand_to } => { @@ -1077,7 +1077,7 @@ impl DefCollector<'_> { ast_id, *expand_to, self.def_map.krate, - &resolver, + &resolver_def_id, &mut |_err| (), ); if let Ok(Ok(call_id)) = call_id { @@ -1093,7 +1093,7 @@ impl DefCollector<'_> { *derive_attr, *derive_pos as u32, self.def_map.krate, - &resolver2, + &resolver, ); if let Ok((macro_id, def_id, call_id)) = id { @@ -1158,7 +1158,7 @@ impl DefCollector<'_> { } } - let def = match resolver(path.clone()) { + let def = match resolver_def_id(path.clone()) { Some(def) if def.is_attribute() => def, _ => return true, }; @@ -1292,7 +1292,8 @@ impl DefCollector<'_> { true }); // Attribute resolution can add unresolved macro invocations, so concatenate the lists. - self.unresolved_macros.extend(macros); + macros.extend(mem::take(&mut self.unresolved_macros)); + self.unresolved_macros = macros; for (module_id, depth, container, macro_call_id) in resolved { self.collect_macro_expansion(module_id, macro_call_id, depth, container); diff --git a/crates/hir-ty/src/chalk_ext.rs b/crates/hir-ty/src/chalk_ext.rs index b0885ab003f7..a9c124b42dc2 100644 --- a/crates/hir-ty/src/chalk_ext.rs +++ b/crates/hir-ty/src/chalk_ext.rs @@ -34,6 +34,7 @@ pub trait TyExt { fn callable_sig(&self, db: &dyn HirDatabase) -> Option; fn strip_references(&self) -> &Ty; + fn strip_reference(&self) -> &Ty; /// If this is a `dyn Trait`, returns that trait. fn dyn_trait(&self) -> Option; @@ -182,6 +183,10 @@ impl TyExt for Ty { t } + fn strip_reference(&self) -> &Ty { + self.as_reference().map_or(self, |(ty, _, _)| ty) + } + fn impl_trait_bounds(&self, db: &dyn HirDatabase) -> Option> { match self.kind(Interner) { TyKind::OpaqueType(opaque_ty_id, subst) => { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d4925455d7bd..8f984210e117 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2769,6 +2769,10 @@ impl Type { self.derived(self.ty.strip_references().clone()) } + pub fn strip_reference(&self) -> Type { + self.derived(self.ty.strip_reference().clone()) + } + pub fn is_unknown(&self) -> bool { self.ty.is_unknown() } diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index fc8f23f19ab9..c84318b2fb87 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -324,6 +324,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_type(ty) } + pub fn resolve_trait(&self, trait_: &ast::Path) -> Option { + self.imp.resolve_trait(trait_) + } + // FIXME: Figure out a nice interface to inspect adjustments pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { self.imp.is_implicit_reborrow(expr) @@ -924,7 +928,12 @@ impl<'db> SemanticsImpl<'db> { } fn original_ast_node(&self, node: N) -> Option { - self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map(|it| it.value) + self.wrap_node_infile(node).original_ast_node(self.db.upcast()).map( + |InFile { file_id, value }| { + self.cache(find_root(value.syntax()), file_id); + value + }, + ) } fn diagnostics_display_range(&self, src: InFile) -> FileRange { @@ -1009,6 +1018,20 @@ impl<'db> SemanticsImpl<'db> { Some(Type::new_with_resolver(self.db, &analyze.resolver, ty)) } + fn resolve_trait(&self, path: &ast::Path) -> Option { + let analyze = self.analyze(path.syntax())?; + let hygiene = hir_expand::hygiene::Hygiene::new(self.db.upcast(), analyze.file_id); + let ctx = body::LowerCtx::with_hygiene(self.db.upcast(), &hygiene); + let hir_path = Path::from_src(path.clone(), &ctx)?; + match analyze + .resolver + .resolve_path_in_type_ns_fully(self.db.upcast(), hir_path.mod_path())? + { + TypeNs::TraitId(id) => Some(Trait { id }), + _ => None, + } + } + fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr) } diff --git a/crates/ide-assists/src/handlers/expand_glob_import.rs b/crates/ide-assists/src/handlers/expand_glob_import.rs index 943c1d90e636..87f5018fb695 100644 --- a/crates/ide-assists/src/handlers/expand_glob_import.rs +++ b/crates/ide-assists/src/handlers/expand_glob_import.rs @@ -36,7 +36,7 @@ use crate::{ // pub struct Baz; // } // -// use foo::{Baz, Bar}; +// use foo::{Bar, Baz}; // // fn qux(bar: Bar, baz: Baz) {} // ``` @@ -281,7 +281,7 @@ mod foo { pub fn f() {} } -use foo::{Baz, Bar, f}; +use foo::{Bar, Baz, f}; fn qux(bar: Bar, baz: Baz) { f(); @@ -351,7 +351,7 @@ mod foo { pub fn f() {} } -use foo::{Baz, Bar, f}; +use foo::{Bar, Baz, f}; fn qux(bar: Bar, baz: Baz) { f(); @@ -440,7 +440,7 @@ mod foo { } } -use foo::{bar::{Baz, Bar, f}, baz::*}; +use foo::{bar::{Bar, Baz, f}, baz::*}; fn qux(bar: Bar, baz: Baz) { f(); @@ -561,7 +561,7 @@ mod foo { use foo::{ bar::{*, f}, - baz::{g, qux::{q, h}} + baz::{g, qux::{h, q}} }; fn qux(bar: Bar, baz: Baz) { diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 658a1aadf53e..80d3b9255936 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -7,7 +7,7 @@ use ide_db::{ imports::insert_use::remove_path_if_in_use_stmt, path_transform::PathTransform, search::{FileReference, SearchScope}, - syntax_helpers::node_ext::expr_as_name_ref, + syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref}, RootDatabase, }; use itertools::{izip, Itertools}; @@ -301,7 +301,16 @@ fn inline( params: &[(ast::Pat, Option, hir::Param)], CallInfo { node, arguments, generic_arg_list }: &CallInfo, ) -> ast::Expr { - let body = fn_body.clone_for_update(); + let body = if sema.hir_file_for(fn_body.syntax()).is_macro() { + cov_mark::hit!(inline_call_defined_in_macro); + if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) { + body + } else { + fn_body.clone_for_update() + } + } else { + fn_body.clone_for_update() + }; let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -1144,6 +1153,41 @@ fn bar() -> u32 { x }) + foo() } +"#, + ) + } + + #[test] + fn inline_call_defined_in_macro() { + cov_mark::check!(inline_call_defined_in_macro); + check_assist( + inline_call, + r#" +macro_rules! define_foo { + () => { fn foo() -> u32 { + let x = 0; + x + } }; +} +define_foo!(); +fn bar() -> u32 { + foo$0() +} +"#, + r#" +macro_rules! define_foo { + () => { fn foo() -> u32 { + let x = 0; + x + } }; +} +define_foo!(); +fn bar() -> u32 { + { + let x = 0; + x + } +} "#, ) } diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index e8d48607be0e..6eaab48a32ba 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -535,7 +535,7 @@ mod foo { pub struct Baz; } -use foo::{Baz, Bar}; +use foo::{Bar, Baz}; fn qux(bar: Bar, baz: Baz) {} "#####, diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 149afcac9d47..72579e6026ae 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -400,7 +400,7 @@ impl Completions { ) { if let PathCompletionCtx { kind: PathKind::Pat { pat_ctx }, .. } = path_ctx { cov_mark::hit!(enum_variant_pattern_path); - self.add_variant_pat(ctx, pat_ctx, variant, local_name); + self.add_variant_pat(ctx, pat_ctx, Some(path_ctx), variant, local_name); return; } @@ -484,12 +484,14 @@ impl Completions { &mut self, ctx: &CompletionContext<'_>, pattern_ctx: &PatternContext, + path_ctx: Option<&PathCompletionCtx>, variant: hir::Variant, local_name: Option, ) { self.add_opt(render_variant_pat( RenderContext::new(ctx), pattern_ctx, + path_ctx, variant, local_name.clone(), None, @@ -504,7 +506,14 @@ impl Completions { path: hir::ModPath, ) { let path = Some(&path); - self.add_opt(render_variant_pat(RenderContext::new(ctx), pattern_ctx, variant, None, path)); + self.add_opt(render_variant_pat( + RenderContext::new(ctx), + pattern_ctx, + None, + variant, + None, + path, + )); } pub(crate) fn add_struct_pat( diff --git a/crates/ide-completion/src/completions/attribute.rs b/crates/ide-completion/src/completions/attribute.rs index 1d8a8c5f20db..d9fe94cb44ee 100644 --- a/crates/ide-completion/src/completions/attribute.rs +++ b/crates/ide-completion/src/completions/attribute.rs @@ -115,7 +115,7 @@ pub(crate) fn complete_attribute_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } let attributes = annotated_item_kind.and_then(|kind| { diff --git a/crates/ide-completion/src/completions/attribute/derive.rs b/crates/ide-completion/src/completions/attribute/derive.rs index 14538fef6072..793c22630bf8 100644 --- a/crates/ide-completion/src/completions/attribute/derive.rs +++ b/crates/ide-completion/src/completions/attribute/derive.rs @@ -97,7 +97,7 @@ pub(crate) fn complete_derive_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index bdf6e64f0969..5d0ddaaf2a22 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -11,7 +11,14 @@ pub(crate) fn complete_expr_path( acc: &mut Completions, ctx: &CompletionContext<'_>, path_ctx @ PathCompletionCtx { qualified, .. }: &PathCompletionCtx, - &ExprCtx { + expr_ctx: &ExprCtx, +) { + let _p = profile::span("complete_expr_path"); + if !ctx.qualifier_ctx.none() { + return; + } + + let &ExprCtx { in_block_expr, in_loop_body, after_if_expr, @@ -23,12 +30,7 @@ pub(crate) fn complete_expr_path( ref impl_, in_match_guard, .. - }: &ExprCtx, -) { - let _p = profile::span("complete_expr_path"); - if !ctx.qualifier_ctx.none() { - return; - } + } = expr_ctx; let wants_mut_token = ref_expr_parent.as_ref().map(|it| it.mut_token().is_none()).unwrap_or(false); @@ -46,11 +48,32 @@ pub(crate) fn complete_expr_path( }; match qualified { - Qualified::Infer => ctx + Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), + Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) + } + Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { + if let Some(hir::Adt::Enum(e)) = ty.as_adt() { + cov_mark::hit!(completes_variant_through_alias); + acc.add_enum_variants(ctx, path_ctx, e); + } + + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. @@ -179,10 +202,21 @@ pub(crate) fn complete_expr_path( } } } - ctx.process_all_names(&mut |name, def| { - if scope_def_applicable(def) { - acc.add_path_resolution(ctx, path_ctx, name, def); + ctx.process_all_names(&mut |name, def| match def { + ScopeDef::ModuleDef(hir::ModuleDef::Trait(t)) => { + let assocs = t.items_with_supertraits(ctx.db); + match &*assocs { + // traits with no assoc items are unusable as expressions since + // there is no associated item path that can be constructed with them + [] => (), + // FIXME: Render the assoc item with the trait qualified + &[_item] => acc.add_path_resolution(ctx, path_ctx, name, def), + // FIXME: Append `::` to the thing here, since a trait on its own won't work + [..] => acc.add_path_resolution(ctx, path_ctx, name, def), + } } + _ if scope_def_applicable(def) => acc.add_path_resolution(ctx, path_ctx, name, def), + _ => (), }); if is_func_update.is_none() { diff --git a/crates/ide-completion/src/completions/item_list.rs b/crates/ide-completion/src/completions/item_list.rs index 4e4c9fba6cc5..60d05ae46b91 100644 --- a/crates/ide-completion/src/completions/item_list.rs +++ b/crates/ide-completion/src/completions/item_list.rs @@ -66,7 +66,7 @@ pub(crate) fn complete_item_list( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::No | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::No | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/pattern.rs b/crates/ide-completion/src/completions/pattern.rs index 17dfe432b352..71d2d9d434b4 100644 --- a/crates/ide-completion/src/completions/pattern.rs +++ b/crates/ide-completion/src/completions/pattern.rs @@ -74,7 +74,7 @@ pub(crate) fn complete_pattern( hir::ModuleDef::Variant(variant) if refutable || single_variant_enum(variant.parent_enum(ctx.db)) => { - acc.add_variant_pat(ctx, pattern_ctx, variant, Some(name.clone())); + acc.add_variant_pat(ctx, pattern_ctx, None, variant, Some(name.clone())); true } hir::ModuleDef::Adt(hir::Adt::Enum(e)) => refutable || single_variant_enum(e), @@ -180,6 +180,6 @@ pub(crate) fn complete_pattern_path( acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} } } diff --git a/crates/ide-completion/src/completions/type.rs b/crates/ide-completion/src/completions/type.rs index 87a998dfcce6..8f9db2f94c20 100644 --- a/crates/ide-completion/src/completions/type.rs +++ b/crates/ide-completion/src/completions/type.rs @@ -49,11 +49,27 @@ pub(crate) fn complete_type_path( }; match qualified { - Qualified::Infer => ctx + Qualified::TypeAnchor { ty: None, trait_: None } => ctx .traits_in_scope() .iter() .flat_map(|&it| hir::Trait::from(it).items(ctx.sema.db)) .for_each(|item| add_assoc_item(acc, item)), + Qualified::TypeAnchor { trait_: Some(trait_), .. } => { + trait_.items(ctx.sema.db).into_iter().for_each(|item| add_assoc_item(acc, item)) + } + Qualified::TypeAnchor { ty: Some(ty), trait_: None } => { + ctx.iterate_path_candidates(&ty, |item| { + add_assoc_item(acc, item); + }); + + // Iterate assoc types separately + ty.iterate_assoc_items(ctx.db, ctx.krate, |item| { + if let hir::AssocItem::TypeAlias(ty) = item { + acc.add_type_alias(ctx, ty) + } + None::<()> + }); + } Qualified::With { resolution: None, .. } => {} Qualified::With { resolution: Some(resolution), .. } => { // Add associated types on type parameters and `Self`. diff --git a/crates/ide-completion/src/completions/use_.rs b/crates/ide-completion/src/completions/use_.rs index bb2ecc9fdde7..2555c34aa747 100644 --- a/crates/ide-completion/src/completions/use_.rs +++ b/crates/ide-completion/src/completions/use_.rs @@ -115,6 +115,6 @@ pub(crate) fn complete_use_path( }); acc.add_nameref_keywords_with_colon(ctx); } - Qualified::Infer | Qualified::With { resolution: None, .. } => {} + Qualified::TypeAnchor { .. } | Qualified::With { resolution: None, .. } => {} } } diff --git a/crates/ide-completion/src/completions/vis.rs b/crates/ide-completion/src/completions/vis.rs index ca8303906a80..5e6cf4bf9a52 100644 --- a/crates/ide-completion/src/completions/vis.rs +++ b/crates/ide-completion/src/completions/vis.rs @@ -29,7 +29,7 @@ pub(crate) fn complete_vis_path( acc.add_super_keyword(ctx, *super_chain_len); } - Qualified::Absolute | Qualified::Infer | Qualified::With { .. } => {} + Qualified::Absolute | Qualified::TypeAnchor { .. } | Qualified::With { .. } => {} Qualified::No => { if !has_in_token { cov_mark::hit!(kw_completion_in); diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index 93b6ad5d145d..e35f79d2b695 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -193,7 +193,10 @@ pub(super) enum Qualified { super_chain_len: Option, }, /// <_>:: - Infer, + TypeAnchor { + ty: Option, + trait_: Option, + }, /// Whether the path is an absolute path Absolute, } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index c71ffa0ed86f..22ec7cead498 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -162,11 +162,52 @@ impl<'a> CompletionContext<'a> { } /// Calculate the expected type and name of the cursor position. - fn expected_type_and_name(&self) -> (Option, Option) { + fn expected_type_and_name( + &self, + name_like: &ast::NameLike, + ) -> (Option, Option) { let mut node = match self.token.parent() { Some(it) => it, None => return (None, None), }; + + let strip_refs = |mut ty: Type| match name_like { + ast::NameLike::NameRef(n) => { + let p = match n.syntax().parent() { + Some(it) => it, + None => return ty, + }; + let top_syn = match_ast! { + match p { + ast::FieldExpr(e) => e + .syntax() + .ancestors() + .map_while(ast::FieldExpr::cast) + .last() + .map(|it| it.syntax().clone()), + ast::PathSegment(e) => e + .syntax() + .ancestors() + .skip(1) + .take_while(|it| ast::Path::can_cast(it.kind()) || ast::PathExpr::can_cast(it.kind())) + .find_map(ast::PathExpr::cast) + .map(|it| it.syntax().clone()), + _ => None + } + }; + let top_syn = match top_syn { + Some(it) => it, + None => return ty, + }; + for _ in top_syn.ancestors().skip(1).map_while(ast::RefExpr::cast) { + cov_mark::hit!(expected_type_fn_param_ref); + ty = ty.strip_reference(); + } + ty + } + _ => ty, + }; + loop { break match_ast! { match node { @@ -199,13 +240,9 @@ impl<'a> CompletionContext<'a> { self.token.clone(), ).map(|ap| { let name = ap.ident().map(NameOrNameRef::Name); - let ty = if has_ref(&self.token) { - cov_mark::hit!(expected_type_fn_param_ref); - ap.ty.remove_ref() - } else { - Some(ap.ty) - }; - (ty, name) + + let ty = strip_refs(ap.ty); + (Some(ty), name) }) .unwrap_or((None, None)) }, @@ -330,8 +367,6 @@ impl<'a> CompletionContext<'a> { return None; } - (self.expected_type, self.expected_name) = self.expected_type_and_name(); - // Overwrite the path kind for derives if let Some((original_file, file_with_fake_ident, offset, origin_attr)) = derive_ctx { if let Some(ast::NameLike::NameRef(name_ref)) = @@ -389,6 +424,7 @@ impl<'a> CompletionContext<'a> { return Some(analysis); } }; + (self.expected_type, self.expected_name) = self.expected_type_and_name(&name_like); let analysis = match name_like { ast::NameLike::Lifetime(lifetime) => CompletionAnalysis::Lifetime( Self::classify_lifetime(&self.sema, original_file, lifetime)?, @@ -556,7 +592,7 @@ impl<'a> CompletionContext<'a> { has_call_parens: false, has_macro_bang: false, qualified: Qualified::No, - parent: path.parent_path(), + parent: None, path: path.clone(), kind: PathKind::Item { kind: ItemListKind::SourceFile }, has_type_args: false, @@ -791,92 +827,125 @@ impl<'a> CompletionContext<'a> { PathKind::Type { location: location.unwrap_or(TypeLocation::Other) } }; + let mut kind_macro_call = |it: ast::MacroCall| { + path_ctx.has_macro_bang = it.excl_token().is_some(); + let parent = it.syntax().parent()?; + // Any path in an item list will be treated as a macro call by the parser + let kind = match_ast! { + match parent { + ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), + ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, + ast::MacroType(ty) => make_path_kind_type(ty.into()), + ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, + ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { + Some(it) => match_ast! { + match it { + ast::Trait(_) => ItemListKind::Trait, + ast::Impl(it) => if it.trait_().is_some() { + ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) + } else { + ItemListKind::Impl + }, + _ => return None + } + }, + None => return None, + } }, + ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, + ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, + _ => return None, + } + }; + Some(kind) + }; + let make_path_kind_attr = |meta: ast::Meta| { + let attr = meta.parent_attr()?; + let kind = attr.kind(); + let attached = attr.syntax().parent()?; + let is_trailing_outer_attr = kind != AttrKind::Inner + && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next) + .is_none(); + let annotated_item_kind = + if is_trailing_outer_attr { None } else { Some(attached.kind()) }; + Some(PathKind::Attr { attr_ctx: AttrCtx { kind, annotated_item_kind } }) + }; + // Infer the path kind let parent = path.syntax().parent()?; let kind = match_ast! { - match parent { - 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))); - } + match parent { + 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))); } } + } - path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); - make_path_kind_expr(it.into()) - }, - ast::TupleStructPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::RecordPat(it) => { - path_ctx.has_call_parens = true; - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::PathPat(it) => { - PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} - }, - ast::MacroCall(it) => { - // A macro call 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()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + // A macro call 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))); + } - path_ctx.has_macro_bang = it.excl_token().is_some(); - let parent = it.syntax().parent()?; - // Any path in an item list will be treated as a macro call by the parser - match_ast! { - match parent { - ast::MacroExpr(expr) => make_path_kind_expr(expr.into()), - ast::MacroPat(it) => PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())}, - ast::MacroType(ty) => make_path_kind_type(ty.into()), - ast::ItemList(_) => PathKind::Item { kind: ItemListKind::Module }, - ast::AssocItemList(_) => PathKind::Item { kind: match parent.parent() { - Some(it) => match_ast! { - match it { - ast::Trait(_) => ItemListKind::Trait, - ast::Impl(it) => if it.trait_().is_some() { - ItemListKind::TraitImpl(find_node_in_file_compensated(sema, original_file, &it)) - } else { - ItemListKind::Impl - }, - _ => return None - } - }, - None => return None, - } }, - ast::ExternItemList(_) => PathKind::Item { kind: ItemListKind::ExternBlock }, - ast::SourceFile(_) => PathKind::Item { kind: ItemListKind::SourceFile }, - _ => return None, - } - } - }, - ast::Meta(meta) => { - let attr = meta.parent_attr()?; - let kind = attr.kind(); - let attached = attr.syntax().parent()?; - let is_trailing_outer_attr = kind != AttrKind::Inner - && non_trivia_sibling(attr.syntax().clone().into(), syntax::Direction::Next).is_none(); - let annotated_item_kind = if is_trailing_outer_attr { - None - } else { - Some(attached.kind()) - }; - PathKind::Attr { - attr_ctx: AttrCtx { - kind, - annotated_item_kind, - } + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + // completing inside a qualifier + ast::Path(parent) => { + path_ctx.parent = Some(parent.clone()); + let parent = iter::successors(Some(parent), |it| it.parent_path()).last()?.syntax().parent()?; + match_ast! { + match parent { + ast::PathType(it) => make_path_kind_type(it.into()), + ast::PathExpr(it) => { + path_ctx.has_call_parens = it.syntax().parent().map_or(false, |it| ast::CallExpr::can_cast(it.kind())); + + make_path_kind_expr(it.into()) + }, + ast::TupleStructPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::RecordPat(it) => { + path_ctx.has_call_parens = true; + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into()) } + }, + ast::PathPat(it) => { + PathKind::Pat { pat_ctx: pattern_context_for(sema, original_file, it.into())} + }, + ast::MacroCall(it) => { + kind_macro_call(it)? + }, + ast::Meta(meta) => make_path_kind_attr(meta)?, + ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, + ast::UseTree(_) => PathKind::Use, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), + _ => return None, } - }, - ast::Visibility(it) => PathKind::Vis { has_in_token: it.in_token().is_some() }, - ast::UseTree(_) => PathKind::Use, - _ => return None, - + } + }, + ast::RecordExpr(it) => make_path_kind_expr(it.into()), + _ => return None, } }; @@ -884,49 +953,53 @@ impl<'a> CompletionContext<'a> { path_ctx.has_type_args = segment.generic_arg_list().is_some(); // calculate the qualifier context - if let Some((path, use_tree_parent)) = path_or_use_tree_qualifier(&path) { + if let Some((qualifier, use_tree_parent)) = path_or_use_tree_qualifier(&path) { path_ctx.use_tree_parent = use_tree_parent; if !use_tree_parent && segment.coloncolon_token().is_some() { path_ctx.qualified = Qualified::Absolute; } else { - let path = path + let qualifier = qualifier .segment() .and_then(|it| find_node_in_file(original_file, &it)) .map(|it| it.parent_path()); - if let Some(path) = path { - // `<_>::$0` - let is_infer_qualifier = path.qualifier().is_none() - && matches!( - path.segment().and_then(|it| it.kind()), - Some(ast::PathSegmentKind::Type { - type_ref: Some(ast::Type::InferType(_)), - trait_ref: None, - }) - ); + if let Some(qualifier) = qualifier { + let type_anchor = match qualifier.segment().and_then(|it| it.kind()) { + Some(ast::PathSegmentKind::Type { + type_ref: Some(type_ref), + trait_ref, + }) if qualifier.qualifier().is_none() => Some((type_ref, trait_ref)), + _ => None, + }; - path_ctx.qualified = if is_infer_qualifier { - Qualified::Infer + path_ctx.qualified = if let Some((ty, trait_ref)) = type_anchor { + let ty = match ty { + ast::Type::InferType(_) => None, + ty => sema.resolve_type(&ty), + }; + let trait_ = trait_ref.and_then(|it| sema.resolve_trait(&it.path()?)); + Qualified::TypeAnchor { ty, trait_ } } else { - let res = sema.resolve_path(&path); + let res = sema.resolve_path(&qualifier); // For understanding how and why super_chain_len is calculated the way it // is check the documentation at it's definition let mut segment_count = 0; - let super_count = iter::successors(Some(path.clone()), |p| p.qualifier()) - .take_while(|p| { - p.segment() - .and_then(|s| { - segment_count += 1; - s.super_token() - }) - .is_some() - }) - .count(); + let super_count = + iter::successors(Some(qualifier.clone()), |p| p.qualifier()) + .take_while(|p| { + p.segment() + .and_then(|s| { + segment_count += 1; + s.super_token() + }) + .is_some() + }) + .count(); let super_chain_len = if segment_count > super_count { None } else { Some(super_count) }; - Qualified::With { path, resolution: res, super_chain_len } + Qualified::With { path: qualifier, resolution: res, super_chain_len } } }; } @@ -1141,19 +1214,6 @@ fn path_or_use_tree_qualifier(path: &ast::Path) -> Option<(ast::Path, bool)> { Some((use_tree.path()?, true)) } -fn has_ref(token: &SyntaxToken) -> bool { - let mut token = token.clone(); - for skip in [SyntaxKind::IDENT, SyntaxKind::WHITESPACE, T![mut]] { - if token.kind() == skip { - token = match token.prev_token() { - Some(it) => it, - None => return false, - } - } - } - token.kind() == T![&] -} - pub(crate) fn is_in_token_of_for_loop(element: SyntaxElement) -> bool { // oh my ... (|| { diff --git a/crates/ide-completion/src/context/tests.rs b/crates/ide-completion/src/context/tests.rs index c5557bdafb33..50845b3881f4 100644 --- a/crates/ide-completion/src/context/tests.rs +++ b/crates/ide-completion/src/context/tests.rs @@ -391,3 +391,23 @@ fn foo($0: Foo) {} expect![[r#"ty: ?, name: ?"#]], ); } + +#[test] +fn expected_type_ref_prefix_on_field() { + check_expected_type_and_name( + r#" +fn foo(_: &mut i32) {} +struct S { + field: i32, +} + +fn main() { + let s = S { + field: 100, + }; + foo(&mut s.f$0); +} +"#, + expect!["ty: i32, name: ?"], + ); +} diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 9b25964a6086..946134b0ff95 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -1271,8 +1271,8 @@ fn main() { st S [] st &mut S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); check_relevance( @@ -1288,8 +1288,8 @@ fn main() { lc s [type+name+local] st S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); check_relevance( @@ -1305,8 +1305,8 @@ fn main() { lc ssss [type+local] st S [type] st S [] - fn main() [] fn foo(…) [] + fn main() [] "#]], ); } @@ -1342,12 +1342,11 @@ fn main() { lc &t [type+local] st S [] st &S [type] - st T [] st S [] - fn main() [] + st T [] fn foo(…) [] + fn main() [] md core [] - tt Sized [] "#]], ) } @@ -1389,12 +1388,11 @@ fn main() { lc &mut t [type+local] st S [] st &mut S [type] - st T [] st S [] - fn main() [] + st T [] fn foo(…) [] + fn main() [] md core [] - tt Sized [] "#]], ) } @@ -1485,14 +1483,13 @@ fn main() { expect![[r#" st S [] st &S [type] - st T [] st S [] - fn main() [] + st T [] fn bar() [] fn &bar() [type] fn foo(…) [] + fn main() [] md core [] - tt Sized [] "#]], ) } @@ -1636,8 +1633,8 @@ fn foo() { ev Foo::B [type_could_unify] fn foo() [] en Foo [] - fn baz() [] fn bar() [] + fn baz() [] "#]], ); } @@ -1727,9 +1724,9 @@ fn f() { } "#, expect![[r#" - md std [] st Buffer [] fn f() [] + md std [] tt BufRead (use std::io::BufRead) [requires_import] st BufReader (use std::io::BufReader) [requires_import] st BufWriter (use std::io::BufWriter) [requires_import] diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 241de0a1834a..4b5535718c5d 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -85,7 +85,9 @@ fn render( item.ref_match(ref_match, path_ctx.path.syntax().text_range().start()); } FuncKind::Method(DotAccess { receiver: Some(receiver), .. }, _) => { - item.ref_match(ref_match, receiver.syntax().text_range().start()); + if let Some(original_expr) = completion.sema.original_ast_node(receiver.clone()) { + item.ref_match(ref_match, original_expr.syntax().text_range().start()); + } } _ => (), } diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 03db08a911e9..34a384f2f7ae 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use syntax::SmolStr; use crate::{ - context::{ParamContext, ParamKind, PatternContext}, + context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext}, render::{ variant::{format_literal_label, visible_fields}, RenderContext, @@ -42,6 +42,7 @@ pub(crate) fn render_struct_pat( pub(crate) fn render_variant_pat( ctx: RenderContext<'_>, pattern_ctx: &PatternContext, + path_ctx: Option<&PathCompletionCtx>, variant: hir::Variant, local_name: Option, path: Option<&hir::ModPath>, @@ -58,9 +59,23 @@ pub(crate) fn render_variant_pat( (name.to_smol_str(), name.escaped().to_smol_str()) } }; - let kind = variant.kind(ctx.db()); - let label = format_literal_label(name.as_str(), kind); - let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; + + let (label, pat) = match path_ctx { + Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()), + _ => { + let kind = variant.kind(ctx.db()); + let label = format_literal_label(name.as_str(), kind); + let pat = render_pat( + &ctx, + pattern_ctx, + &escaped_name, + kind, + &visible_fields, + fields_omitted, + )?; + (label, pat) + } + }; Some(build_completion(ctx, label, pat, variant)) } diff --git a/crates/ide-completion/src/tests.rs b/crates/ide-completion/src/tests.rs index 4be6acbe8461..cf826648dcf7 100644 --- a/crates/ide-completion/src/tests.rs +++ b/crates/ide-completion/src/tests.rs @@ -23,8 +23,6 @@ mod type_pos; mod use_tree; mod visibility; -use std::mem; - use hir::{db::DefDatabase, PrefixKind, Semantics}; use ide_db::{ base_db::{fixture::ChangeFixture, FileLoader, FilePosition}, @@ -107,12 +105,9 @@ fn completion_list_with_config( ) -> String { // filter out all but one builtintype completion for smaller test outputs let items = get_all_items(config, ra_fixture, trigger_character); - let mut bt_seen = false; let items = items .into_iter() - .filter(|it| { - it.kind() != CompletionItemKind::BuiltinType || !mem::replace(&mut bt_seen, true) - }) + .filter(|it| it.kind() != CompletionItemKind::BuiltinType || it.label() == "u32") .filter(|it| include_keywords || it.kind() != CompletionItemKind::Keyword) .filter(|it| include_keywords || it.kind() != CompletionItemKind::Snippet) .sorted_by_key(|it| (it.kind(), it.label().to_owned(), it.detail().map(ToOwned::to_owned))) diff --git a/crates/ide-completion/src/tests/expression.rs b/crates/ide-completion/src/tests/expression.rs index ce9d01d337ba..925081ebf660 100644 --- a/crates/ide-completion/src/tests/expression.rs +++ b/crates/ide-completion/src/tests/expression.rs @@ -44,7 +44,6 @@ fn baz() { st Record st Tuple st Unit - tt Trait un Union ev TupleV(…) TupleV(u32) bt u32 @@ -137,7 +136,6 @@ impl Unit { st Record st Tuple st Unit - tt Trait tp TypeParam un Union ev TupleV(…) TupleV(u32) @@ -653,3 +651,22 @@ fn main() { "]], ); } + +#[test] +fn complete_record_expr_path() { + check( + r#" +struct Zulu; +impl Zulu { + fn test() -> Self { } +} +fn boi(val: Zulu) { } +fn main() { + boi(Zulu:: $0 {}); +} +"#, + expect![[r#" + fn test() fn() -> Zulu + "#]], + ); +} diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 877b5f216433..30ddbe2dc6f6 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -443,7 +443,7 @@ fn foo() { } "#, expect![[r#" - bn TupleVariant(…) TupleVariant($1)$0 + bn TupleVariant TupleVariant "#]], ); check_empty( @@ -458,7 +458,7 @@ fn foo() { } "#, expect![[r#" - bn RecordVariant {…} RecordVariant { field$1 }$0 + bn RecordVariant RecordVariant "#]], ); } diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index ec32602fa3c2..f6accc68e5e8 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -167,7 +167,6 @@ fn main() { st Foo st Foo {…} Foo { foo1: u32, foo2: u32 } tt Default - tt Sized bt u32 kw crate:: kw self:: diff --git a/crates/ide-completion/src/tests/special.rs b/crates/ide-completion/src/tests/special.rs index ca779c2fc713..033dc99c26cf 100644 --- a/crates/ide-completion/src/tests/special.rs +++ b/crates/ide-completion/src/tests/special.rs @@ -674,7 +674,60 @@ fn bar() -> Bar { expect![[r#" fn foo() (as Foo) fn() -> Self "#]], - ) + ); +} + +#[test] +fn type_anchor_type() { + check( + r#" +trait Foo { + fn foo() -> Self; +} +struct Bar; +impl Bar { + fn bar() {} +} +impl Foo for Bar { + fn foo() -> { + Bar + } +} +fn bar() -> Bar { + ::$0 +} +"#, + expect![[r#" + fn bar() fn() + fn foo() (as Foo) fn() -> Self + "#]], + ); +} + +#[test] +fn type_anchor_type_trait() { + check( + r#" +trait Foo { + fn foo() -> Self; +} +struct Bar; +impl Bar { + fn bar() {} +} +impl Foo for Bar { + fn foo() -> { + Bar + } +} +fn bar() -> Bar { + ::$0 +} +"#, + expect![[r#" + fn foo() (as Foo) fn() -> Self + "#]], + ); } #[test] diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs index d013d6f4b19f..3fb49b45d988 100644 --- a/crates/ide/src/syntax_highlighting.rs +++ b/crates/ide/src/syntax_highlighting.rs @@ -13,7 +13,7 @@ mod html; #[cfg(test)] mod tests; -use hir::{InFile, Name, Semantics}; +use hir::{Name, Semantics}; use ide_db::{FxHashMap, RootDatabase}; use syntax::{ ast, AstNode, AstToken, NodeOrToken, SyntaxKind::*, SyntaxNode, TextRange, WalkEvent, T, @@ -325,7 +325,7 @@ fn traverse( Leave(NodeOrToken::Node(node)) => { // Doc comment highlighting injection, we do this when leaving the node // so that we overwrite the highlighting of the doc comment itself. - inject::doc_comment(hl, sema, InFile::new(file_id.into(), &node)); + inject::doc_comment(hl, sema, file_id, &node); continue; } }; diff --git a/crates/ide/src/syntax_highlighting/inject.rs b/crates/ide/src/syntax_highlighting/inject.rs index f779a985a99a..f376f9fda7a5 100644 --- a/crates/ide/src/syntax_highlighting/inject.rs +++ b/crates/ide/src/syntax_highlighting/inject.rs @@ -5,7 +5,8 @@ use std::mem; use either::Either; use hir::{InFile, Semantics}; use ide_db::{ - active_parameter::ActiveParameter, defs::Definition, rust_doc::is_rust_fence, SymbolKind, + active_parameter::ActiveParameter, base_db::FileId, defs::Definition, rust_doc::is_rust_fence, + SymbolKind, }; use syntax::{ ast::{self, AstNode, IsString, QuoteOffsets}, @@ -81,16 +82,18 @@ pub(super) fn ra_fixture( const RUSTDOC_FENCE_LENGTH: usize = 3; const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"]; -/// Injection of syntax highlighting of doctests. +/// Injection of syntax highlighting of doctests and intra doc links. pub(super) fn doc_comment( hl: &mut Highlights, sema: &Semantics<'_, RootDatabase>, - InFile { file_id: src_file_id, value: node }: InFile<&SyntaxNode>, + src_file_id: FileId, + node: &SyntaxNode, ) { let (attributes, def) = match doc_attributes(sema, node) { Some(it) => it, None => return, }; + let src_file_id = src_file_id.into(); // Extract intra-doc links and emit highlights for them. if let Some((docs, doc_mapping)) = attributes.docs_with_rangemap(sema.db) { diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html new file mode 100644 index 000000000000..8a1d69816e68 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_inline.html @@ -0,0 +1,51 @@ + + +
//! [Struct]
+//! This is an intra doc injection test for modules
+//! [Struct]
+//! This is an intra doc injection test for modules
+
+pub struct Struct;
+
\ No newline at end of file diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html new file mode 100644 index 000000000000..c4c3e3dc2606 --- /dev/null +++ b/crates/ide/src/syntax_highlighting/test_data/highlight_module_docs_outline.html @@ -0,0 +1,50 @@ + + +
/// [crate::foo::Struct]
+/// This is an intra doc injection test for modules
+/// [crate::foo::Struct]
+/// This is an intra doc injection test for modules
+mod foo;
+
\ 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 6ba6153178da..99be7c664868 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -915,6 +915,52 @@ fn main() { } #[test] +fn test_mod_hl_injection() { + check_highlighting( + r##" +//- /foo.rs +//! [Struct] +//! This is an intra doc injection test for modules +//! [Struct] +//! This is an intra doc injection test for modules + +pub struct Struct; +//- /lib.rs crate:foo +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +mod foo; +"##, + expect_file!["./test_data/highlight_module_docs_inline.html"], + false, + ); + check_highlighting( + r##" +//- /lib.rs crate:foo +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +/// [crate::foo::Struct] +/// This is an intra doc injection test for modules +mod foo; +//- /foo.rs +//! [Struct] +//! This is an intra doc injection test for modules +//! [Struct] +//! This is an intra doc injection test for modules + +pub struct Struct; +"##, + expect_file!["./test_data/highlight_module_docs_outline.html"], + false, + ); +} + +#[test] +#[cfg_attr( + all(unix, not(target_pointer_width = "64")), + ignore = "depends on `DefaultHasher` outputs" +)] fn test_rainbow_highlighting() { check_highlighting( r#" diff --git a/crates/limit/src/lib.rs b/crates/limit/src/lib.rs index 3c1da80edb98..d6a706a7cd73 100644 --- a/crates/limit/src/lib.rs +++ b/crates/limit/src/lib.rs @@ -2,12 +2,13 @@ #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] +#[cfg(feature = "tracking")] use std::sync::atomic::AtomicUsize; /// Represents a struct used to enforce a numerical limit. pub struct Limit { upper_bound: usize, - #[allow(unused)] + #[cfg(feature = "tracking")] max: AtomicUsize, } @@ -15,14 +16,22 @@ impl Limit { /// Creates a new limit. #[inline] pub const fn new(upper_bound: usize) -> Self { - Self { upper_bound, max: AtomicUsize::new(0) } + Self { + upper_bound, + #[cfg(feature = "tracking")] + max: AtomicUsize::new(0), + } } /// Creates a new limit. #[inline] #[cfg(feature = "tracking")] pub const fn new_tracking(upper_bound: usize) -> Self { - Self { upper_bound, max: AtomicUsize::new(1) } + Self { + upper_bound, + #[cfg(feature = "tracking")] + max: AtomicUsize::new(1), + } } /// Gets the underlying numeric limit. diff --git a/crates/parser/src/grammar/paths.rs b/crates/parser/src/grammar/paths.rs index f9efcef92a61..8de5d33a1936 100644 --- a/crates/parser/src/grammar/paths.rs +++ b/crates/parser/src/grammar/paths.rs @@ -54,7 +54,7 @@ fn path_for_qualifier( mut qual: CompletedMarker, ) -> CompletedMarker { loop { - let use_tree = matches!(p.nth(2), T![*] | T!['{']); + let use_tree = mode == Mode::Use && matches!(p.nth(2), T![*] | T!['{']); if p.at(T![::]) && !use_tree { let path = qual.precede(p); p.bump(T![::]); diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 4b1858b8ed89..4c205b9cadac 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -39,6 +39,8 @@ pub(crate) struct ProcMacroSrv { expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, } +const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; + impl ProcMacroSrv { pub fn expand(&mut self, task: ExpandMacro) -> Result { let expander = self.expander(task.lib.as_ref()).map_err(|err| { @@ -66,13 +68,18 @@ impl ProcMacroSrv { // FIXME: replace this with std's scoped threads once they stabilize // (then remove dependency on crossbeam) let result = crossbeam::scope(|s| { - let res = s + let res = match s + .builder() + .stack_size(EXPANDER_STACK_SIZE) + .name(task.macro_name.clone()) .spawn(|_| { expander .expand(&task.macro_name, ¯o_body, attributes.as_ref()) .map(|it| FlatTree::new(&it)) - }) - .join(); + }) { + Ok(handle) => handle.join(), + Err(e) => std::panic::resume_unwind(Box::new(e)), + }; match res { Ok(res) => res, diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 0ada4b73e842..5d1c013c3275 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -66,7 +66,9 @@ pub fn load_workspace( }; let crate_graph = ws.to_crate_graph( - &mut |_, path: &AbsPath| load_proc_macro(proc_macro_client.as_ref(), path, &[]), + &mut |_, path: &AbsPath| { + load_proc_macro(proc_macro_client.as_ref().map_err(|e| &**e), path, &[]) + }, &mut |path: &AbsPath| { let contents = loader.load_sync(path); let path = vfs::VfsPath::from(path.to_path_buf()); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 9ae361b034e2..eaab275bc68a 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -303,6 +303,9 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); + let standalone_server_name = + format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); + if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { self.proc_macro_clients = self @@ -316,10 +319,8 @@ impl GlobalState { tracing::info!("Found a cargo workspace..."); if let Some(sysroot) = sysroot.as_ref() { tracing::info!("Found a cargo workspace with a sysroot..."); - let server_path = sysroot - .root() - .join("libexec") - .join("rust-analyzer-proc-macro-srv"); + let server_path = + sysroot.root().join("libexec").join(&standalone_server_name); if std::fs::metadata(&server_path).is_ok() { tracing::info!( "And the server exists at {}", @@ -389,7 +390,10 @@ impl GlobalState { let mut crate_graph = CrateGraph::default(); for (idx, ws) in self.workspaces.iter().enumerate() { - let proc_macro_client = self.proc_macro_clients[idx].as_ref(); + let proc_macro_client = match self.proc_macro_clients.get(idx) { + Some(res) => res.as_ref().map_err(|e| &**e), + None => Err("Proc macros are disabled"), + }; let mut load_proc_macro = move |crate_name: &str, path: &AbsPath| { load_proc_macro( proc_macro_client, @@ -573,7 +577,7 @@ impl SourceRootConfig { /// Load the proc-macros for the given lib path, replacing all expanders whose names are in `dummy_replace` /// with an identity dummy expander. pub(crate) fn load_proc_macro( - server: Result<&ProcMacroServer, &String>, + server: Result<&ProcMacroServer, &str>, path: &AbsPath, dummy_replace: &[Box], ) -> ProcMacroLoadResult { diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index cf90ba64cff1..63309a155219 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -880,7 +880,6 @@ impl ForExpr { pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } pub fn pat(&self) -> Option { support::child(&self.syntax) } pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } - pub fn iterable(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -890,7 +889,6 @@ pub struct IfExpr { impl ast::HasAttrs for IfExpr {} impl IfExpr { pub fn if_token(&self) -> Option { support::token(&self.syntax, T![if]) } - pub fn condition(&self) -> Option { support::child(&self.syntax) } pub fn else_token(&self) -> Option { support::token(&self.syntax, T![else]) } } @@ -1051,7 +1049,6 @@ pub struct WhileExpr { impl ast::HasAttrs for WhileExpr {} impl WhileExpr { pub fn while_token(&self) -> Option { support::token(&self.syntax, T![while]) } - pub fn condition(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1170,7 +1167,6 @@ pub struct MatchGuard { } impl MatchGuard { pub fn if_token(&self) -> Option { support::token(&self.syntax, T![if]) } - pub fn condition(&self) -> Option { support::child(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs index b143df1f83f2..bb92c51e9a90 100644 --- a/crates/syntax/src/ast/node_ext.rs +++ b/crates/syntax/src/ast/node_ext.rs @@ -806,6 +806,19 @@ impl ast::GenericParamList { } } +impl ast::ForExpr { + pub fn iterable(&self) -> Option { + // If the iterable is a BlockExpr, check if the body is missing. + // If it is assume the iterable is the expression that is missing instead. + let mut exprs = support::children(self.syntax()); + let first = exprs.next(); + match first { + Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first), + first => first, + } + } +} + impl ast::HasLoopBody for ast::ForExpr { fn loop_body(&self) -> Option { let mut exprs = support::children(self.syntax()); @@ -815,6 +828,19 @@ impl ast::HasLoopBody for ast::ForExpr { } } +impl ast::WhileExpr { + pub fn condition(&self) -> Option { + // If the condition is a BlockExpr, check if the body is missing. + // If it is assume the condition is the expression that is missing instead. + let mut exprs = support::children(self.syntax()); + let first = exprs.next(); + match first { + Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first), + first => first, + } + } +} + impl ast::HasLoopBody for ast::WhileExpr { fn loop_body(&self) -> Option { let mut exprs = support::children(self.syntax()); @@ -835,3 +861,15 @@ impl From for ast::Item { } } } + +impl ast::IfExpr { + pub fn condition(&self) -> Option { + support::child(&self.syntax) + } +} + +impl ast::MatchGuard { + pub fn condition(&self) -> Option { + support::child(&self.syntax) + } +} diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index 4cfb8075cb15..6d2766225103 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -682,6 +682,8 @@ fn lower_rule(acc: &mut Vec, grammar: &Grammar, label: Option<&String>, r | "value" | "trait" | "self_ty" + | "iterable" + | "condition" ); if manually_implemented { return; diff --git a/docs/dev/README.md b/docs/dev/README.md index 468f2b9e981f..76bbd1e91889 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -210,7 +210,8 @@ Release process is handled by `release`, `dist` and `promote` xtasks, `release` ./rust-rust-analyzer # Note the name! ``` -Additionally, it assumes that the remote for `rust-analyzer` is called `upstream` (I use `origin` to point to my fork). +The remote for `rust-analyzer` must be called `upstream` (I use `origin` to point to my fork). +In addition, for `xtask promote` (see below), `rust-rust-analyzer` must have a `rust-analyzer` remote pointing to this repository on GitHub. `release` calls the GitHub API calls to scrape pull request comments and categorize them in the changelog. This step uses the `curl` and `jq` applications, which need to be available in `PATH`. @@ -225,13 +226,13 @@ Release steps: * push it to `upstream`. This triggers GitHub Actions which: * runs `cargo xtask dist` to package binaries and VS Code extension * makes a GitHub release - * pushes VS Code extension to the marketplace + * publishes the VS Code extension to the marketplace * call the GitHub API for PR details * create a new changelog in `rust-analyzer.github.io` 3. While the release is in progress, fill in the changelog 4. Commit & push the changelog 5. Tweet -6. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's submodule. +6. Inside `rust-analyzer`, run `cargo xtask promote` -- this will create a PR to rust-lang/rust updating rust-analyzer's subtree. Self-approve the PR. If the GitHub Actions release fails because of a transient problem like a timeout, you can re-run the job from the Actions console. diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 1c5fc64c2417..17ada5156407 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -77,18 +77,12 @@ impl flags::Promote { cmd!(sh, "git switch master").run()?; cmd!(sh, "git fetch upstream").run()?; cmd!(sh, "git reset --hard upstream/master").run()?; - cmd!(sh, "git submodule update --recursive").run()?; let date = date_iso(sh)?; let branch = format!("rust-analyzer-{date}"); cmd!(sh, "git switch -c {branch}").run()?; - { - let _dir = sh.push_dir("src/tools/rust-analyzer"); - cmd!(sh, "git fetch origin").run()?; - cmd!(sh, "git reset --hard origin/release").run()?; - } - cmd!(sh, "git add src/tools/rust-analyzer").run()?; - cmd!(sh, "git commit -m':arrow_up: rust-analyzer'").run()?; + cmd!(sh, "git subtree pull -P src/tools/rust-analyzer rust-analyzer master").run()?; + if !self.dry_run { cmd!(sh, "git push -u origin {branch}").run()?; cmd!( From 2c7f2c105bb52d83409fc29bd181af442b8ba43d Mon Sep 17 00:00:00 2001 From: Nika Layzell Date: Mon, 25 Jul 2022 00:43:33 -0400 Subject: [PATCH 03/22] proc_macro/bridge: send diagnostics over the bridge as a struct This removes some RPC when creating and emitting diagnostics, and simplifies the bridge slightly. After this change, there are no remaining methods which take advantage of the support for `&mut` references to objects in the store as arguments, meaning that support for them could technically be removed if we wanted. The only remaining uses of immutable references into the store are `TokenStream` and `SourceFile`. --- .../src/abis/abi_sysroot/ra_server.rs | 59 ++----------------- 1 file changed, 4 insertions(+), 55 deletions(-) diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs index 46882845a807..c1f8615aaf20 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs @@ -37,23 +37,6 @@ pub struct SourceFile { type Level = super::proc_macro::Level; type LineColumn = super::proc_macro::LineColumn; -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec, - children: Vec, -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } -} - pub struct FreeFunctions; #[derive(Default)] @@ -65,8 +48,6 @@ impl server::Types for RustAnalyzer { type FreeFunctions = FreeFunctions; type TokenStream = TokenStream; type SourceFile = SourceFile; - type MultiSpan = Vec; - type Diagnostic = Diagnostic; type Span = Span; type Symbol = Symbol; } @@ -90,6 +71,10 @@ impl server::FreeFunctions for RustAnalyzer { span: tt::TokenId::unspecified(), }) } + + fn emit_diagnostic(&mut self, _: bridge::Diagnostic) { + // FIXME handle diagnostic + } } impl server::TokenStream for RustAnalyzer { @@ -282,30 +267,6 @@ impl server::SourceFile for RustAnalyzer { } } -impl server::Diagnostic for RustAnalyzer { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level, msg); - diag.spans = spans; - diag - } - - fn sub( - &mut self, - _diag: &mut Self::Diagnostic, - _level: Level, - _msg: &str, - _spans: Self::MultiSpan, - ) { - // FIXME handle diagnostic - // - } - - fn emit(&mut self, _diag: Self::Diagnostic) { - // FIXME handle diagnostic - // diag.emit() - } -} - impl server::Span for RustAnalyzer { fn debug(&mut self, span: Self::Span) -> String { format!("{:?}", span.0) @@ -372,18 +333,6 @@ impl server::Span for RustAnalyzer { } } -impl server::MultiSpan for RustAnalyzer { - fn new(&mut self) -> Self::MultiSpan { - // FIXME handle span - vec![] - } - - fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) { - //TODP - other.push(span) - } -} - impl server::Symbol for RustAnalyzer { fn normalize_and_validate_ident(&mut self, string: &str) -> Result { // FIXME: nfc-normalize and validate idents From 22c8c9c4014c63309524f218d92554ebfdf9621e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 9 Aug 2022 07:23:57 +0300 Subject: [PATCH 04/22] :arrow_up: rust-analyzer --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- .github/workflows/release.yaml | 16 +- README.md | 2 +- crates/flycheck/src/lib.rs | 15 +- crates/hir-def/src/generics.rs | 2 +- crates/hir-def/src/item_tree.rs | 2 +- crates/hir-def/src/visibility.rs | 2 +- crates/hir-expand/src/fixup.rs | 110 ++++- crates/hir-expand/src/name.rs | 2 + crates/hir-ty/src/infer/expr.rs | 63 +-- crates/hir-ty/src/lower.rs | 2 +- crates/hir-ty/src/method_resolution.rs | 51 ++- crates/hir/src/semantics.rs | 40 ++ crates/hir/src/source_analyzer.rs | 120 +++++- .../src/handlers/generate_enum_variant.rs | 375 +++++++++++++++++- crates/ide-db/src/apply_change.rs | 2 +- crates/ide-db/src/defs.rs | 80 ++++ crates/ide-ssr/src/lib.rs | 2 +- crates/ide/src/call_hierarchy.rs | 4 +- crates/ide/src/expand_macro.rs | 4 +- crates/ide/src/goto_definition.rs | 122 +++++- crates/ide/src/goto_implementation.rs | 2 +- crates/ide/src/highlight_related.rs | 3 +- crates/ide/src/hover.rs | 22 +- crates/ide/src/hover/tests.rs | 34 ++ crates/ide/src/inlay_hints.rs | 2 +- crates/ide/src/join_lines.rs | 2 +- crates/ide/src/matching_brace.rs | 2 +- crates/ide/src/moniker.rs | 2 +- crates/ide/src/move_item.rs | 4 +- crates/ide/src/parent_module.rs | 2 +- crates/ide/src/runnables.rs | 81 +++- crates/ide/src/shuffle_crate_graph.rs | 2 +- crates/ide/src/static_index.rs | 2 +- crates/ide/src/status.rs | 2 +- crates/ide/src/syntax_highlighting/tests.rs | 2 +- crates/ide/src/syntax_tree.rs | 2 +- crates/ide/src/view_crate_graph.rs | 2 +- crates/ide/src/view_hir.rs | 2 +- crates/ide/src/view_item_tree.rs | 2 +- crates/paths/src/lib.rs | 8 + crates/proc-macro-api/src/lib.rs | 2 +- crates/proc-macro-srv/src/abis/mod.rs | 2 +- crates/project-model/src/cargo_workspace.rs | 2 +- crates/rust-analyzer/src/diagnostics.rs | 26 +- crates/rust-analyzer/src/global_state.rs | 34 +- crates/rust-analyzer/src/handlers.rs | 4 +- .../src/integrated_benchmarks.rs | 4 +- crates/rust-analyzer/src/main_loop.rs | 89 ++++- crates/rust-analyzer/src/reload.rs | 70 ++-- crates/rust-analyzer/tests/slow-tests/tidy.rs | 7 +- crates/sourcegen/src/lib.rs | 14 +- crates/syntax/src/ast/edit_in_place.rs | 171 ++++++-- crates/syntax/src/ast/make.rs | 5 +- crates/syntax/src/lib.rs | 2 +- crates/vfs-notify/src/lib.rs | 7 +- docs/dev/README.md | 8 +- docs/dev/architecture.md | 2 +- docs/dev/guide.md | 2 +- docs/user/manual.adoc | 4 +- editors/code/package.json | 63 +-- editors/code/src/config.ts | 14 +- editors/code/src/main.ts | 4 +- lib/la-arena/src/lib.rs | 12 +- lib/la-arena/src/map.rs | 169 +++++++- xtask/src/release.rs | 2 +- 66 files changed, 1636 insertions(+), 281 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7ba06356a37c..a038dce3248a 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -19,7 +19,7 @@ Before submitting, please make sure that you're not running into one of these kn Otherwise please try to provide information which will help us to fix the issue faster. Minimal reproducible examples with few dependencies are especially lovely <3. --> -**rust-analyzer version**: (eg. output of "Rust Analyzer: Show RA Version" command) +**rust-analyzer version**: (eg. output of "rust-analyzer: Show RA Version" command, accessible in VSCode via Ctrl/⌘+Shift+P) **rustc version**: (eg. output of `rustc -V`) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4e62f2cde279..ca8eb1309de3 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,6 +18,7 @@ env: FETCH_DEPTH: 0 # pull in the tags for the version string MACOSX_DEPLOYMENT_TARGET: 10.15 CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc + CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc jobs: dist: @@ -36,6 +37,9 @@ jobs: - os: ubuntu-18.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 + - os: ubuntu-18.04 + target: arm-unknown-linux-gnueabihf + code-target: linux-armhf - os: macos-11 target: x86_64-apple-darwin code-target: darwin-x64 @@ -67,13 +71,17 @@ jobs: node-version: 14.x - name: Update apt repositories - if: matrix.target == 'aarch64-unknown-linux-gnu' + if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' run: sudo apt-get update - - name: Install target toolchain + - name: Install AArch64 target toolchain if: matrix.target == 'aarch64-unknown-linux-gnu' run: sudo apt-get install gcc-aarch64-linux-gnu + - name: Install ARM target toolchain + if: matrix.target == 'arm-unknown-linux-gnueabihf' + run: sudo apt-get install gcc-arm-linux-gnueabihf + - name: Dist run: cargo xtask dist --client-patch-version ${{ github.run_number }} @@ -204,6 +212,10 @@ jobs: with: name: dist-aarch64-unknown-linux-gnu path: dist + - uses: actions/download-artifact@v1 + with: + name: dist-arm-unknown-linux-gnueabihf + path: dist - uses: actions/download-artifact@v1 with: name: dist-x86_64-pc-windows-msvc diff --git a/README.md b/README.md index 8bb0517ed5ab..8c3f6f8468bf 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ https://rust-lang.zulipchat.com/#narrow/stream/185405-t-compiler.2Frust-analyzer ## License -Rust analyzer is primarily distributed under the terms of both the MIT +rust-analyzer is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0). See LICENSE-APACHE and LICENSE-MIT for details. diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 4e8bc881ae73..3347940ec6d6 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -57,6 +57,7 @@ pub struct FlycheckHandle { // XXX: drop order is significant sender: Sender, _thread: jod_thread::JoinHandle, + id: usize, } impl FlycheckHandle { @@ -72,18 +73,22 @@ impl FlycheckHandle { .name("Flycheck".to_owned()) .spawn(move || actor.run(receiver)) .expect("failed to spawn thread"); - FlycheckHandle { sender, _thread: thread } + FlycheckHandle { id, sender, _thread: thread } } /// Schedule a re-start of the cargo check worker. pub fn update(&self) { self.sender.send(Restart).unwrap(); } + + pub fn id(&self) -> usize { + self.id + } } pub enum Message { /// Request adding a diagnostic with fixes included to a file - AddDiagnostic { workspace_root: AbsPathBuf, diagnostic: Diagnostic }, + AddDiagnostic { id: usize, workspace_root: AbsPathBuf, diagnostic: Diagnostic }, /// Request check progress notification to client Progress { @@ -96,8 +101,9 @@ pub enum Message { impl fmt::Debug for Message { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Message::AddDiagnostic { workspace_root, diagnostic } => f + Message::AddDiagnostic { id, workspace_root, diagnostic } => f .debug_struct("AddDiagnostic") + .field("id", id) .field("workspace_root", workspace_root) .field("diagnostic_code", &diagnostic.code.as_ref().map(|it| &it.code)) .finish(), @@ -183,7 +189,7 @@ impl FlycheckActor { } } Event::CheckEvent(None) => { - tracing::debug!("flycheck finished"); + tracing::debug!(flycheck_id = self.id, "flycheck finished"); // Watcher finished let cargo_handle = self.cargo_handle.take().unwrap(); @@ -203,6 +209,7 @@ impl FlycheckActor { CargoMessage::Diagnostic(msg) => { self.send(Message::AddDiagnostic { + id: self.id, workspace_root: self.workspace_root.clone(), diagnostic: msg, }); diff --git a/crates/hir-def/src/generics.rs b/crates/hir-def/src/generics.rs index 2397cf501584..469b28c2d9ed 100644 --- a/crates/hir-def/src/generics.rs +++ b/crates/hir-def/src/generics.rs @@ -451,7 +451,7 @@ impl HasChildSource for GenericDefId { if let GenericDefId::TraitId(id) = *self { let trait_ref = id.lookup(db).source(db).value; let idx = idx_iter.next().unwrap(); - params.insert(idx, Either::Right(trait_ref)) + params.insert(idx, Either::Right(trait_ref)); } if let Some(generic_params_list) = generic_params_list { diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 375587ee935b..3342d4db4aa6 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -14,7 +14,7 @@ //! unaffected, so we don't have to recompute name resolution results or item data (see `data.rs`). //! //! The `ItemTree` for the currently open file can be displayed by using the VS Code command -//! "Rust Analyzer: Debug ItemTree". +//! "rust-analyzer: Debug ItemTree". //! //! Compared to rustc's architecture, `ItemTree` has properties from both rustc's AST and HIR: many //! syntax-level Rust features are already desugared to simpler forms in the `ItemTree`, but name diff --git a/crates/hir-def/src/visibility.rs b/crates/hir-def/src/visibility.rs index 6e22a877a9fa..087268a9ecee 100644 --- a/crates/hir-def/src/visibility.rs +++ b/crates/hir-def/src/visibility.rs @@ -224,7 +224,7 @@ pub(crate) fn field_visibilities_query( let resolver = variant_id.module(db).resolver(db); let mut res = ArenaMap::default(); for (field_id, field_data) in var_data.fields().iter() { - res.insert(field_id, field_data.visibility.resolve(db, &resolver)) + res.insert(field_id, field_data.visibility.resolve(db, &resolver)); } Arc::new(res) } diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 9999790fae72..e46f43a878fe 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -5,7 +5,7 @@ use std::mem; use mbe::{SyntheticToken, SyntheticTokenId, TokenMap}; use rustc_hash::FxHashMap; use syntax::{ - ast::{self, AstNode}, + ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, }; use tt::Subtree; @@ -142,8 +142,59 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { ]); } }, + ast::WhileExpr(it) => { + if it.condition().is_none() { + // insert placeholder token after the while token + let while_token = match it.while_token() { + Some(t) => t, + None => continue, + }; + append.insert(while_token.into(), vec![ + SyntheticToken { + kind: SyntaxKind::IDENT, + text: "__ra_fixup".into(), + range: end_range, + id: EMPTY_ID, + }, + ]); + } + if it.loop_body().is_none() { + append.insert(node.clone().into(), vec![ + SyntheticToken { + kind: SyntaxKind::L_CURLY, + text: "{".into(), + range: end_range, + id: EMPTY_ID, + }, + SyntheticToken { + kind: SyntaxKind::R_CURLY, + text: "}".into(), + range: end_range, + id: EMPTY_ID, + }, + ]); + } + }, + ast::LoopExpr(it) => { + if it.loop_body().is_none() { + append.insert(node.clone().into(), vec![ + SyntheticToken { + kind: SyntaxKind::L_CURLY, + text: "{".into(), + range: end_range, + id: EMPTY_ID, + }, + SyntheticToken { + kind: SyntaxKind::R_CURLY, + text: "}".into(), + range: end_range, + id: EMPTY_ID, + }, + ]); + } + }, // FIXME: foo:: - // FIXME: for, loop, match etc. + // FIXME: for, match etc. _ => (), } } @@ -376,6 +427,61 @@ fn foo() { // the {} gets parsed as the condition, I think? expect![[r#" fn foo () {if {} {}} +"#]], + ) + } + + #[test] + fn fixup_while_1() { + check( + r#" +fn foo() { + while +} +"#, + expect![[r#" +fn foo () {while __ra_fixup {}} +"#]], + ) + } + + #[test] + fn fixup_while_2() { + check( + r#" +fn foo() { + while foo +} +"#, + expect![[r#" +fn foo () {while foo {}} +"#]], + ) + } + #[test] + fn fixup_while_3() { + check( + r#" +fn foo() { + while {} +} +"#, + expect![[r#" +fn foo () {while __ra_fixup {}} +"#]], + ) + } + + #[test] + fn fixup_loop() { + check( + r#" +fn foo() { + loop +} +"#, + expect![[r#" +fn foo () {loop {}} "#]], ) } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 85b0a7735fe9..47d191822d84 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -381,6 +381,7 @@ pub mod known { bitor, bitxor_assign, bitxor, + branch, deref_mut, deref, div_assign, @@ -396,6 +397,7 @@ pub mod known { not, owned_box, partial_ord, + poll, r#fn, rem_assign, rem, diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index d164e64a8be0..2a13106390d9 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -10,13 +10,13 @@ use chalk_ir::{ cast::Cast, fold::Shift, DebruijnIndex, GenericArgData, Mutability, TyVariableKind, }; use hir_def::{ - expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Ordering, Statement, UnaryOp}, + expr::{ArithOp, Array, BinaryOp, CmpOp, Expr, ExprId, Literal, Statement, UnaryOp}, generics::TypeOrConstParamData, path::{GenericArg, GenericArgs}, resolver::resolver_for_expr, - ConstParamId, FieldId, FunctionId, ItemContainerId, Lookup, + ConstParamId, FieldId, ItemContainerId, Lookup, }; -use hir_expand::name::{name, Name}; +use hir_expand::name::Name; use stdx::always; use syntax::ast::RangeOp; @@ -28,7 +28,7 @@ use crate::{ const_or_path_to_chalk, generic_arg_to_chalk, lower_to_chalk_mutability, ParamLoweringMode, }, mapping::{from_chalk, ToChalk}, - method_resolution::{self, VisibleFromModule}, + method_resolution::{self, lang_names_for_bin_op, VisibleFromModule}, primitive::{self, UintTy}, static_lifetime, to_chalk_trait_id, utils::{generics, Generics}, @@ -947,7 +947,9 @@ impl<'a> InferenceContext<'a> { let lhs_ty = self.infer_expr(lhs, &lhs_expectation); let rhs_ty = self.table.new_type_var(); - let func = self.resolve_binop_method(op); + let func = lang_names_for_bin_op(op).and_then(|(name, lang_item)| { + self.db.trait_data(self.resolve_lang_item(lang_item)?.as_trait()?).method_by_name(&name) + }); let func = match func { Some(func) => func, None => { @@ -1473,55 +1475,4 @@ impl<'a> InferenceContext<'a> { }, }) } - - fn resolve_binop_method(&self, op: BinaryOp) -> Option { - let (name, lang_item) = match op { - BinaryOp::LogicOp(_) => return None, - BinaryOp::ArithOp(aop) => match aop { - ArithOp::Add => (name!(add), name!(add)), - ArithOp::Mul => (name!(mul), name!(mul)), - ArithOp::Sub => (name!(sub), name!(sub)), - ArithOp::Div => (name!(div), name!(div)), - ArithOp::Rem => (name!(rem), name!(rem)), - ArithOp::Shl => (name!(shl), name!(shl)), - ArithOp::Shr => (name!(shr), name!(shr)), - ArithOp::BitXor => (name!(bitxor), name!(bitxor)), - ArithOp::BitOr => (name!(bitor), name!(bitor)), - ArithOp::BitAnd => (name!(bitand), name!(bitand)), - }, - BinaryOp::Assignment { op: Some(aop) } => match aop { - ArithOp::Add => (name!(add_assign), name!(add_assign)), - ArithOp::Mul => (name!(mul_assign), name!(mul_assign)), - ArithOp::Sub => (name!(sub_assign), name!(sub_assign)), - ArithOp::Div => (name!(div_assign), name!(div_assign)), - ArithOp::Rem => (name!(rem_assign), name!(rem_assign)), - ArithOp::Shl => (name!(shl_assign), name!(shl_assign)), - ArithOp::Shr => (name!(shr_assign), name!(shr_assign)), - ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)), - ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)), - ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)), - }, - BinaryOp::CmpOp(cop) => match cop { - CmpOp::Eq { negated: false } => (name!(eq), name!(eq)), - CmpOp::Eq { negated: true } => (name!(ne), name!(eq)), - CmpOp::Ord { ordering: Ordering::Less, strict: false } => { - (name!(le), name!(partial_ord)) - } - CmpOp::Ord { ordering: Ordering::Less, strict: true } => { - (name!(lt), name!(partial_ord)) - } - CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { - (name!(ge), name!(partial_ord)) - } - CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { - (name!(gt), name!(partial_ord)) - } - }, - BinaryOp::Assignment { op: None } => return None, - }; - - let trait_ = self.resolve_lang_item(lang_item)?.as_trait()?; - - self.db.trait_data(trait_).method_by_name(&name) - } } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 3ed9c941f479..239f66bcb7e7 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -1126,7 +1126,7 @@ pub(crate) fn field_types_query( let ctx = TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); for (field_id, field_data) in var_data.fields().iter() { - res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref))) + res.insert(field_id, make_binders(db, &generics, ctx.lower_ty(&field_data.type_ref))); } Arc::new(res) } diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 15df7b3dd2b9..64622545f840 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -336,7 +336,7 @@ impl InherentImpls { } } -pub fn inherent_impl_crates_query( +pub(crate) fn inherent_impl_crates_query( db: &dyn HirDatabase, krate: CrateId, fp: TyFingerprint, @@ -419,6 +419,55 @@ pub fn def_crates( } } +pub fn lang_names_for_bin_op(op: syntax::ast::BinaryOp) -> Option<(Name, Name)> { + use hir_expand::name; + use syntax::ast::{ArithOp, BinaryOp, CmpOp, Ordering}; + Some(match op { + BinaryOp::LogicOp(_) => return None, + BinaryOp::ArithOp(aop) => match aop { + ArithOp::Add => (name!(add), name!(add)), + ArithOp::Mul => (name!(mul), name!(mul)), + ArithOp::Sub => (name!(sub), name!(sub)), + ArithOp::Div => (name!(div), name!(div)), + ArithOp::Rem => (name!(rem), name!(rem)), + ArithOp::Shl => (name!(shl), name!(shl)), + ArithOp::Shr => (name!(shr), name!(shr)), + ArithOp::BitXor => (name!(bitxor), name!(bitxor)), + ArithOp::BitOr => (name!(bitor), name!(bitor)), + ArithOp::BitAnd => (name!(bitand), name!(bitand)), + }, + BinaryOp::Assignment { op: Some(aop) } => match aop { + ArithOp::Add => (name!(add_assign), name!(add_assign)), + ArithOp::Mul => (name!(mul_assign), name!(mul_assign)), + ArithOp::Sub => (name!(sub_assign), name!(sub_assign)), + ArithOp::Div => (name!(div_assign), name!(div_assign)), + ArithOp::Rem => (name!(rem_assign), name!(rem_assign)), + ArithOp::Shl => (name!(shl_assign), name!(shl_assign)), + ArithOp::Shr => (name!(shr_assign), name!(shr_assign)), + ArithOp::BitXor => (name!(bitxor_assign), name!(bitxor_assign)), + ArithOp::BitOr => (name!(bitor_assign), name!(bitor_assign)), + ArithOp::BitAnd => (name!(bitand_assign), name!(bitand_assign)), + }, + BinaryOp::CmpOp(cop) => match cop { + CmpOp::Eq { negated: false } => (name!(eq), name!(eq)), + CmpOp::Eq { negated: true } => (name!(ne), name!(eq)), + CmpOp::Ord { ordering: Ordering::Less, strict: false } => { + (name!(le), name!(partial_ord)) + } + CmpOp::Ord { ordering: Ordering::Less, strict: true } => { + (name!(lt), name!(partial_ord)) + } + CmpOp::Ord { ordering: Ordering::Greater, strict: false } => { + (name!(ge), name!(partial_ord)) + } + CmpOp::Ord { ordering: Ordering::Greater, strict: true } => { + (name!(gt), name!(partial_ord)) + } + }, + BinaryOp::Assignment { op: None } => return None, + }) +} + /// Look up the method with the given name. pub(crate) fn lookup_method( ty: &Canonical, diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index c84318b2fb87..416b6f58061d 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -357,6 +357,26 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_method_call(call).map(Function::from) } + pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { + self.imp.resolve_await_to_poll(await_expr).map(Function::from) + } + + pub fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + self.imp.resolve_prefix_expr(prefix_expr).map(Function::from) + } + + pub fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + self.imp.resolve_index_expr(index_expr).map(Function::from) + } + + pub fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + self.imp.resolve_bin_expr(bin_expr).map(Function::from) + } + + pub fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + self.imp.resolve_try_expr(try_expr).map(Function::from) + } + pub fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.imp.resolve_method_call_as_callable(call) } @@ -1066,6 +1086,26 @@ impl<'db> SemanticsImpl<'db> { self.analyze(call.syntax())?.resolve_method_call(self.db, call) } + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { + self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr) + } + + fn resolve_prefix_expr(&self, prefix_expr: &ast::PrefixExpr) -> Option { + self.analyze(prefix_expr.syntax())?.resolve_prefix_expr(self.db, prefix_expr) + } + + fn resolve_index_expr(&self, index_expr: &ast::IndexExpr) -> Option { + self.analyze(index_expr.syntax())?.resolve_index_expr(self.db, index_expr) + } + + fn resolve_bin_expr(&self, bin_expr: &ast::BinExpr) -> Option { + self.analyze(bin_expr.syntax())?.resolve_bin_expr(self.db, bin_expr) + } + + fn resolve_try_expr(&self, try_expr: &ast::TryExpr) -> Option { + self.analyze(try_expr.syntax())?.resolve_try_expr(self.db, try_expr) + } + fn resolve_method_call_as_callable(&self, call: &ast::MethodCallExpr) -> Option { self.analyze(call.syntax())?.resolve_method_call_as_callable(self.db, call) } diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 1eb51b20c356..f5e2e4430709 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -25,15 +25,20 @@ use hir_def::{ Lookup, ModuleDefId, VariantId, }; use hir_expand::{ - builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, name::AsName, HirFileId, InFile, + builtin_fn_macro::BuiltinFnLikeExpander, + hygiene::Hygiene, + name, + name::{AsName, Name}, + HirFileId, InFile, }; use hir_ty::{ diagnostics::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, UnsafeExpr, }, - method_resolution, Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, - TyExt, TyKind, TyLoweringContext, + method_resolution::{self, lang_names_for_bin_op}, + Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, + TyLoweringContext, }; use itertools::Itertools; use smallvec::SmallVec; @@ -255,8 +260,90 @@ impl SourceAnalyzer { ) -> Option { let expr_id = self.expr_id(db, &call.clone().into())?; let (f_in_trait, substs) = self.infer.as_ref()?.method_resolution(expr_id)?; - let f_in_impl = self.resolve_impl_method(db, f_in_trait, &substs); - f_in_impl.or(Some(f_in_trait)) + + Some(self.resolve_impl_method_or_trait_def(db, f_in_trait, &substs)) + } + + pub(crate) fn resolve_await_to_poll( + &self, + db: &dyn HirDatabase, + await_expr: &ast::AwaitExpr, + ) -> Option { + let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?; + + let op_fn = db + .lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())? + .as_function()?; + let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_prefix_expr( + &self, + db: &dyn HirDatabase, + prefix_expr: &ast::PrefixExpr, + ) -> Option { + let lang_item_name = match prefix_expr.op_kind()? { + ast::UnaryOp::Deref => name![deref], + ast::UnaryOp::Not => name![not], + ast::UnaryOp::Neg => name![neg], + }; + let ty = self.ty_of_expr(db, &prefix_expr.expr()?.into())?; + + let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?; + let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_index_expr( + &self, + db: &dyn HirDatabase, + index_expr: &ast::IndexExpr, + ) -> Option { + let base_ty = self.ty_of_expr(db, &index_expr.base()?.into())?; + let index_ty = self.ty_of_expr(db, &index_expr.index()?.into())?; + + let lang_item_name = name![index]; + + let op_fn = self.lang_trait_fn(db, &lang_item_name, &lang_item_name)?; + let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn) + .push(base_ty.clone()) + .push(index_ty.clone()) + .build(); + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_bin_expr( + &self, + db: &dyn HirDatabase, + binop_expr: &ast::BinExpr, + ) -> Option { + let op = binop_expr.op_kind()?; + let lhs = self.ty_of_expr(db, &binop_expr.lhs()?.into())?; + let rhs = self.ty_of_expr(db, &binop_expr.rhs()?.into())?; + + let op_fn = lang_names_for_bin_op(op) + .and_then(|(name, lang_item)| self.lang_trait_fn(db, &lang_item, &name))?; + let substs = + hir_ty::TyBuilder::subst_for_def(db, op_fn).push(lhs.clone()).push(rhs.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + } + + pub(crate) fn resolve_try_expr( + &self, + db: &dyn HirDatabase, + try_expr: &ast::TryExpr, + ) -> Option { + let ty = self.ty_of_expr(db, &try_expr.expr()?.into())?; + + let op_fn = + db.lang_item(self.resolver.krate(), name![branch].to_smol_str())?.as_function()?; + let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build(); + + Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) } pub(crate) fn resolve_field( @@ -666,6 +753,29 @@ impl SourceAnalyzer { let fun_data = db.function_data(func); method_resolution::lookup_impl_method(self_ty, db, trait_env, impled_trait, &fun_data.name) } + + fn resolve_impl_method_or_trait_def( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: &Substitution, + ) -> FunctionId { + self.resolve_impl_method(db, func, substs).unwrap_or(func) + } + + fn lang_trait_fn( + &self, + db: &dyn HirDatabase, + lang_trait: &Name, + method_name: &Name, + ) -> Option { + db.trait_data(db.lang_item(self.resolver.krate(), lang_trait.to_smol_str())?.as_trait()?) + .method_by_name(method_name) + } + + fn ty_of_expr(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<&Ty> { + self.infer.as_ref()?.type_of_expr.get(self.expr_id(db, &expr)?) + } } fn scope_for( diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs index 4461fbd5ac82..35cd42908af2 100644 --- a/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -1,8 +1,8 @@ -use hir::{HasSource, InFile}; +use hir::{HasSource, HirDisplay, InFile}; use ide_db::assists::{AssistId, AssistKind}; use syntax::{ - ast::{self, edit::IndentLevel}, - AstNode, TextSize, + ast::{self, make, HasArgList}, + match_ast, AstNode, SyntaxNode, }; use crate::assist_context::{AssistContext, Assists}; @@ -32,8 +32,8 @@ use crate::assist_context::{AssistContext, Assists}; // } // ``` pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let path_expr: ast::PathExpr = ctx.find_node_at_offset()?; - let path = path_expr.path()?; + let path: ast::Path = ctx.find_node_at_offset()?; + let parent = path_parent(&path)?; if ctx.sema.resolve_path(&path).is_some() { // No need to generate anything if the path resolves @@ -50,26 +50,71 @@ pub(crate) fn generate_enum_variant(acc: &mut Assists, ctx: &AssistContext<'_>) ctx.sema.resolve_path(&path.qualifier()?) { let target = path.syntax().text_range(); - return add_variant_to_accumulator(acc, ctx, target, e, &name_ref); + return add_variant_to_accumulator(acc, ctx, target, e, &name_ref, parent); } None } +#[derive(Debug)] +enum PathParent { + PathExpr(ast::PathExpr), + RecordExpr(ast::RecordExpr), + PathPat(ast::PathPat), + UseTree(ast::UseTree), +} + +impl PathParent { + fn syntax(&self) -> &SyntaxNode { + match self { + PathParent::PathExpr(it) => it.syntax(), + PathParent::RecordExpr(it) => it.syntax(), + PathParent::PathPat(it) => it.syntax(), + PathParent::UseTree(it) => it.syntax(), + } + } + + fn make_field_list(&self, ctx: &AssistContext<'_>) -> Option { + let scope = ctx.sema.scope(self.syntax())?; + + match self { + PathParent::PathExpr(it) => { + if let Some(call_expr) = it.syntax().parent().and_then(ast::CallExpr::cast) { + make_tuple_field_list(call_expr, ctx, &scope) + } else { + None + } + } + PathParent::RecordExpr(it) => make_record_field_list(it, ctx, &scope), + PathParent::UseTree(_) | PathParent::PathPat(_) => None, + } + } +} + +fn path_parent(path: &ast::Path) -> Option { + let parent = path.syntax().parent()?; + + match_ast! { + match parent { + ast::PathExpr(it) => Some(PathParent::PathExpr(it)), + ast::RecordExpr(it) => Some(PathParent::RecordExpr(it)), + ast::PathPat(it) => Some(PathParent::PathPat(it)), + ast::UseTree(it) => Some(PathParent::UseTree(it)), + _ => None + } + } +} + fn add_variant_to_accumulator( acc: &mut Assists, ctx: &AssistContext<'_>, target: syntax::TextRange, adt: hir::Enum, name_ref: &ast::NameRef, + parent: PathParent, ) -> Option<()> { let db = ctx.db(); let InFile { file_id, value: enum_node } = adt.source(db)?.original_ast_node(db)?; - let enum_indent = IndentLevel::from_node(&enum_node.syntax()); - - let variant_list = enum_node.variant_list()?; - let offset = variant_list.syntax().text_range().end() - TextSize::of('}'); - let empty_enum = variant_list.variants().next().is_none(); acc.add( AssistId("generate_enum_variant", AssistKind::Generate), @@ -77,18 +122,80 @@ fn add_variant_to_accumulator( target, |builder| { builder.edit_file(file_id.original_file(db)); - let text = format!( - "{maybe_newline}{indent_1}{name},\n{enum_indent}", - maybe_newline = if empty_enum { "\n" } else { "" }, - indent_1 = IndentLevel(1), - name = name_ref, - enum_indent = enum_indent - ); - builder.insert(offset, text) + let node = builder.make_mut(enum_node); + let variant = make_variant(ctx, name_ref, parent); + node.variant_list().map(|it| it.add_variant(variant.clone_for_update())); }, ) } +fn make_variant( + ctx: &AssistContext<'_>, + name_ref: &ast::NameRef, + parent: PathParent, +) -> ast::Variant { + let field_list = parent.make_field_list(ctx); + make::variant(make::name(&name_ref.text()), field_list) +} + +fn make_record_field_list( + record: &ast::RecordExpr, + ctx: &AssistContext<'_>, + scope: &hir::SemanticsScope<'_>, +) -> Option { + let fields = record.record_expr_field_list()?.fields(); + let record_fields = fields.map(|field| { + let name = name_from_field(&field); + + let ty = field + .expr() + .and_then(|it| expr_ty(ctx, it, scope)) + .unwrap_or_else(make::ty_placeholder); + + make::record_field(None, name, ty) + }); + Some(make::record_field_list(record_fields).into()) +} + +fn name_from_field(field: &ast::RecordExprField) -> ast::Name { + let text = match field.name_ref() { + Some(it) => it.to_string(), + None => name_from_field_shorthand(field).unwrap_or("unknown".to_string()), + }; + make::name(&text) +} + +fn name_from_field_shorthand(field: &ast::RecordExprField) -> Option { + let path = match field.expr()? { + ast::Expr::PathExpr(path_expr) => path_expr.path(), + _ => None, + }?; + Some(path.as_single_name_ref()?.to_string()) +} + +fn make_tuple_field_list( + call_expr: ast::CallExpr, + ctx: &AssistContext<'_>, + scope: &hir::SemanticsScope<'_>, +) -> Option { + let args = call_expr.arg_list()?.args(); + let tuple_fields = args.map(|arg| { + let ty = expr_ty(ctx, arg, &scope).unwrap_or_else(make::ty_placeholder); + make::tuple_field(None, ty) + }); + Some(make::tuple_field_list(tuple_fields).into()) +} + +fn expr_ty( + ctx: &AssistContext<'_>, + arg: ast::Expr, + scope: &hir::SemanticsScope<'_>, +) -> Option { + let ty = ctx.sema.type_of_expr(&arg).map(|it| it.adjusted())?; + let text = ty.display_source_code(ctx.db(), scope.module().into()).ok()?; + Some(make::ty(&text)) +} + #[cfg(test)] mod tests { use crate::tests::{check_assist, check_assist_not_applicable}; @@ -221,6 +328,236 @@ mod m { fn main() { m::Foo::Baz } +", + ) + } + + #[test] + fn associated_single_element_tuple() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + Foo::Bar$0(true) +} +", + r" +enum Foo { + Bar(bool), +} +fn main() { + Foo::Bar(true) +} +", + ) + } + + #[test] + fn associated_single_element_tuple_unknown_type() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + Foo::Bar$0(x) +} +", + r" +enum Foo { + Bar(_), +} +fn main() { + Foo::Bar(x) +} +", + ) + } + + #[test] + fn associated_multi_element_tuple() { + check_assist( + generate_enum_variant, + r" +struct Struct {} +enum Foo {} +fn main() { + Foo::Bar$0(true, x, Struct {}) +} +", + r" +struct Struct {} +enum Foo { + Bar(bool, _, Struct), +} +fn main() { + Foo::Bar(true, x, Struct {}) +} +", + ) + } + + #[test] + fn associated_record() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + Foo::$0Bar { x: true } +} +", + r" +enum Foo { + Bar { x: bool }, +} +fn main() { + Foo::Bar { x: true } +} +", + ) + } + + #[test] + fn associated_record_unknown_type() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + Foo::$0Bar { x: y } +} +", + r" +enum Foo { + Bar { x: _ }, +} +fn main() { + Foo::Bar { x: y } +} +", + ) + } + + #[test] + fn associated_record_field_shorthand() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + let x = true; + Foo::$0Bar { x } +} +", + r" +enum Foo { + Bar { x: bool }, +} +fn main() { + let x = true; + Foo::Bar { x } +} +", + ) + } + + #[test] + fn associated_record_field_shorthand_unknown_type() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn main() { + Foo::$0Bar { x } +} +", + r" +enum Foo { + Bar { x: _ }, +} +fn main() { + Foo::Bar { x } +} +", + ) + } + + #[test] + fn associated_record_field_multiple_fields() { + check_assist( + generate_enum_variant, + r" +struct Struct {} +enum Foo {} +fn main() { + Foo::$0Bar { x, y: x, s: Struct {} } +} +", + r" +struct Struct {} +enum Foo { + Bar { x: _, y: _, s: Struct }, +} +fn main() { + Foo::Bar { x, y: x, s: Struct {} } +} +", + ) + } + + #[test] + fn use_tree() { + check_assist( + generate_enum_variant, + r" +//- /main.rs +mod foo; +use foo::Foo::Bar$0; + +//- /foo.rs +enum Foo {} +", + r" +enum Foo { + Bar, +} +", + ) + } + + #[test] + fn not_applicable_for_path_type() { + check_assist_not_applicable( + generate_enum_variant, + r" +enum Foo {} +impl Foo::Bar$0 {} +", + ) + } + + #[test] + fn path_pat() { + check_assist( + generate_enum_variant, + r" +enum Foo {} +fn foo(x: Foo) { + match x { + Foo::Bar$0 => + } +} +", + r" +enum Foo { + Bar, +} +fn foo(x: Foo) { + match x { + Foo::Bar => + } +} ", ) } diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index 98b0e9c947a7..f8134c552f75 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -45,7 +45,7 @@ impl RootDatabase { // |=== // | Editor | Action Name // - // | VS Code | **Rust Analyzer: Memory Usage (Clears Database)** + // | VS Code | **rust-analyzer: Memory Usage (Clears Database)** // |=== // image::https://user-images.githubusercontent.com/48062697/113065592-08559f00-91b1-11eb-8c96-64b88068ec02.gif[] pub fn per_query_memory_usage(&mut self) -> Vec<(String, Bytes)> { diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs index aeaca00ec65c..6c13c039723b 100644 --- a/crates/ide-db/src/defs.rs +++ b/crates/ide-db/src/defs.rs @@ -127,10 +127,12 @@ impl Definition { } } +// FIXME: IdentClass as a name no longer fits #[derive(Debug)] pub enum IdentClass { NameClass(NameClass), NameRefClass(NameRefClass), + Operator(OperatorClass), } impl IdentClass { @@ -147,6 +149,11 @@ impl IdentClass { .map(IdentClass::NameClass) .or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass)) }, + ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator), + ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator), + ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator), + ast::PrefixExpr(prefix_expr) => OperatorClass::classify_prefix(sema,&prefix_expr).map(IdentClass::Operator), + ast::TryExpr(try_expr) => OperatorClass::classify_try(sema,&try_expr).map(IdentClass::Operator), _ => None, } } @@ -184,6 +191,33 @@ impl IdentClass { res.push(Definition::Local(local_ref)); res.push(Definition::Field(field_ref)); } + IdentClass::Operator( + OperatorClass::Await(func) + | OperatorClass::Prefix(func) + | OperatorClass::Bin(func) + | OperatorClass::Index(func) + | OperatorClass::Try(func), + ) => res.push(Definition::Function(func)), + } + res + } + + pub fn definitions_no_ops(self) -> ArrayVec { + let mut res = ArrayVec::new(); + match self { + IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => { + res.push(it) + } + IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => { + res.push(Definition::Local(local_def)); + res.push(Definition::Field(field_ref)); + } + IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it), + IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => { + res.push(Definition::Local(local_ref)); + res.push(Definition::Field(field_ref)); + } + IdentClass::Operator(_) => (), } res } @@ -332,6 +366,52 @@ impl NameClass { } } +#[derive(Debug)] +pub enum OperatorClass { + Await(Function), + Prefix(Function), + Index(Function), + Try(Function), + Bin(Function), +} + +impl OperatorClass { + pub fn classify_await( + sema: &Semantics<'_, RootDatabase>, + await_expr: &ast::AwaitExpr, + ) -> Option { + sema.resolve_await_to_poll(await_expr).map(OperatorClass::Await) + } + + pub fn classify_prefix( + sema: &Semantics<'_, RootDatabase>, + prefix_expr: &ast::PrefixExpr, + ) -> Option { + sema.resolve_prefix_expr(prefix_expr).map(OperatorClass::Prefix) + } + + pub fn classify_try( + sema: &Semantics<'_, RootDatabase>, + try_expr: &ast::TryExpr, + ) -> Option { + sema.resolve_try_expr(try_expr).map(OperatorClass::Try) + } + + pub fn classify_index( + sema: &Semantics<'_, RootDatabase>, + index_expr: &ast::IndexExpr, + ) -> Option { + sema.resolve_index_expr(index_expr).map(OperatorClass::Index) + } + + pub fn classify_bin( + sema: &Semantics<'_, RootDatabase>, + bin_expr: &ast::BinExpr, + ) -> Option { + sema.resolve_bin_expr(bin_expr).map(OperatorClass::Bin) + } +} + /// This is similar to [`NameClass`], but works for [`ast::NameRef`] rather than /// for [`ast::Name`]. Similarly, what looks like a reference in syntax is a /// reference most of the time, but there are a couple of annoying exceptions. diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs index a5e24daa9fa0..739e0ccb436d 100644 --- a/crates/ide-ssr/src/lib.rs +++ b/crates/ide-ssr/src/lib.rs @@ -57,7 +57,7 @@ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Structural Search Replace** +// | VS Code | **rust-analyzer: Structural Search Replace** // |=== // // Also available as an assist, by writing a comment containing the structural diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs index a18a6bea9798..5a8cda8fb3dd 100644 --- a/crates/ide/src/call_hierarchy.rs +++ b/crates/ide/src/call_hierarchy.rs @@ -7,7 +7,7 @@ use ide_db::{ search::FileReference, FxIndexMap, RootDatabase, }; -use syntax::{ast, AstNode, SyntaxKind::NAME, TextRange}; +use syntax::{ast, AstNode, SyntaxKind::IDENT, TextRange}; use crate::{goto_definition, FilePosition, NavigationTarget, RangeInfo, TryToNav}; @@ -79,7 +79,7 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio let file = sema.parse(file_id); let file = file.syntax(); let token = pick_best_token(file.token_at_offset(position.offset), |kind| match kind { - NAME => 1, + IDENT => 1, _ => 0, })?; let mut calls = CallLocations::default(); diff --git a/crates/ide/src/expand_macro.rs b/crates/ide/src/expand_macro.rs index efa8551a00d5..93252339cd4a 100644 --- a/crates/ide/src/expand_macro.rs +++ b/crates/ide/src/expand_macro.rs @@ -19,7 +19,7 @@ pub struct ExpandedMacro { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Expand macro recursively** +// | VS Code | **rust-analyzer: Expand macro recursively** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020648-b3973180-917a-11eb-84a9-ecb921293dc5.gif[] @@ -32,7 +32,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option< _ => 0, })?; - // due to how Rust Analyzer works internally, we need to special case derive attributes, + // due to how rust-analyzer works internally, we need to special case derive attributes, // otherwise they might not get found, e.g. here with the cursor at $0 `#[attr]` would expand: // ``` // #[attr] diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index d9c97751c95c..b2123b9a8793 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -39,7 +39,11 @@ pub(crate) fn goto_definition( | T![super] | T![crate] | T![Self] - | COMMENT => 2, + | COMMENT => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, + T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, })?; @@ -1628,6 +1632,122 @@ macro_rules! foo { } foo!(bar$0); +"#, + ); + } + + #[test] + fn goto_await_poll() { + check( + r#" +//- minicore: future + +struct MyFut; + +impl core::future::Future for MyFut { + type Output = (); + + fn poll( + //^^^^ + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_> + ) -> std::task::Poll + { + () + } +} + +fn f() { + MyFut.await$0; +} +"#, + ); + } + + #[test] + fn goto_try_op() { + check( + r#" +//- minicore: try + +struct Struct; + +impl core::ops::Try for Struct { + fn branch( + //^^^^^^ + self + ) {} +} + +fn f() { + Struct?$0; +} +"#, + ); + } + + #[test] + fn goto_index_op() { + check( + r#" +//- minicore: index + +struct Struct; + +impl core::ops::Index for Struct { + fn index( + //^^^^^ + self + ) {} +} + +fn f() { + Struct[0]$0; +} +"#, + ); + } + + #[test] + fn goto_prefix_op() { + check( + r#" +//- minicore: deref + +struct Struct; + +impl core::ops::Deref for Struct { + fn deref( + //^^^^^ + self + ) {} +} + +fn f() { + $0*Struct; +} +"#, + ); + } + + #[test] + fn goto_bin_op() { + check( + r#" +//- minicore: add + +struct Struct; + +impl core::ops::Add for Struct { + fn add( + //^^^ + self + ) {} +} + +fn f() { + Struct +$0 Struct; +} "#, ); } diff --git a/crates/ide/src/goto_implementation.rs b/crates/ide/src/goto_implementation.rs index 04b51c839407..b3f711b6b88c 100644 --- a/crates/ide/src/goto_implementation.rs +++ b/crates/ide/src/goto_implementation.rs @@ -30,7 +30,7 @@ pub(crate) fn goto_implementation( let original_token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind { - IDENT | T![self] => 1, + IDENT | T![self] | INT_NUMBER => 1, _ => 0, })?; let range = original_token.text_range(); diff --git a/crates/ide/src/highlight_related.rs b/crates/ide/src/highlight_related.rs index f2d7029eab19..f190da326e45 100644 --- a/crates/ide/src/highlight_related.rs +++ b/crates/ide/src/highlight_related.rs @@ -333,7 +333,8 @@ fn cover_range(r0: Option, r1: Option) -> Option, token: SyntaxToken) -> FxHashSet { sema.descend_into_macros(token) .into_iter() - .filter_map(|token| IdentClass::classify_token(sema, &token).map(IdentClass::definitions)) + .filter_map(|token| IdentClass::classify_token(sema, &token)) + .map(IdentClass::definitions_no_ops) .flatten() .collect() } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 59c97f2dcf96..3ada181f1ed2 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -9,7 +9,7 @@ use either::Either; use hir::{HasSource, Semantics}; use ide_db::{ base_db::FileRange, - defs::{Definition, IdentClass}, + defs::{Definition, IdentClass, OperatorClass}, famous_defs::FamousDefs, helpers::pick_best_token, FxIndexSet, RootDatabase, @@ -101,7 +101,10 @@ pub(crate) fn hover( let offset = range.start(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { - IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 3, + IDENT | INT_NUMBER | LIFETIME_IDENT | T![self] | T![super] | T![crate] | T![Self] => 4, + // index and prefix ops + T!['['] | T![']'] | T![?] | T![*] | T![-] | T![!] => 3, + kind if kind.is_keyword() => 2, T!['('] | T![')'] => 2, kind if kind.is_trivia() => 0, _ => 1, @@ -136,6 +139,11 @@ pub(crate) fn hover( .filter_map(|token| { let node = token.parent()?; let class = IdentClass::classify_token(sema, token)?; + if let IdentClass::Operator(OperatorClass::Await(_)) = class { + // It's better for us to fall back to the keyword hover here, + // rendering poll is very confusing + return None; + } Some(class.definitions().into_iter().zip(iter::once(node).cycle())) }) .flatten() @@ -232,10 +240,12 @@ fn hover_type_fallback( token: &SyntaxToken, original_token: &SyntaxToken, ) -> Option> { - let node = token - .parent_ancestors() - .take_while(|it| !ast::Item::can_cast(it.kind())) - .find(|n| ast::Expr::can_cast(n.kind()) || ast::Pat::can_cast(n.kind()))?; + let node = + token.parent_ancestors().take_while(|it| !ast::Item::can_cast(it.kind())).find(|n| { + ast::Expr::can_cast(n.kind()) + || ast::Pat::can_cast(n.kind()) + || ast::Type::can_cast(n.kind()) + })?; let expr_or_pat = match_ast! { match node { diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index 867d1f54d4fe..c6274264b8f1 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -5051,3 +5051,37 @@ fn f() { ```"#]], ); } + +#[test] +fn hover_deref() { + check( + r#" +//- minicore: deref + +struct Struct(usize); + +impl core::ops::Deref for Struct { + type Target = usize; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +fn f() { + $0*Struct(0); +} +"#, + expect![[r#" + *** + + ```rust + test::Struct + ``` + + ```rust + fn deref(&self) -> &Self::Target + ``` + "#]], + ); +} diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 5aae669aa4d6..ed19784d1fa4 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -100,7 +100,7 @@ pub enum InlayTooltip { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Toggle inlay hints* +// | VS Code | **rust-analyzer: Toggle inlay hints* // |=== // // image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[] diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs index 08621addeef4..edc48e84d725 100644 --- a/crates/ide/src/join_lines.rs +++ b/crates/ide/src/join_lines.rs @@ -28,7 +28,7 @@ pub struct JoinLinesConfig { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Join lines** +// | VS Code | **rust-analyzer: Join lines** // |=== // // image::https://user-images.githubusercontent.com/48062697/113020661-b6922200-917a-11eb-87c4-b75acc028f11.gif[] diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs index da70cecdd8e1..6e8a6d020cc7 100644 --- a/crates/ide/src/matching_brace.rs +++ b/crates/ide/src/matching_brace.rs @@ -12,7 +12,7 @@ use syntax::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Find matching brace** +// | VS Code | **rust-analyzer: Find matching brace** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065573-04298180-91b1-11eb-8dec-d4e2a202f304.gif[] diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 6bab9fa1ebbb..4f758967b461 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -90,7 +90,7 @@ pub(crate) fn moniker( .descend_into_macros(original_token.clone()) .into_iter() .filter_map(|token| { - IdentClass::classify_token(sema, &token).map(IdentClass::definitions).map(|it| { + IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops).map(|it| { it.into_iter().flat_map(|def| def_to_moniker(sema.db, def, current_crate)) }) }) diff --git a/crates/ide/src/move_item.rs b/crates/ide/src/move_item.rs index 02e9fb8b5e28..ffc4bdd7da33 100644 --- a/crates/ide/src/move_item.rs +++ b/crates/ide/src/move_item.rs @@ -19,8 +19,8 @@ pub enum Direction { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Move item up** -// | VS Code | **Rust Analyzer: Move item down** +// | VS Code | **rust-analyzer: Move item up** +// | VS Code | **rust-analyzer: Move item down** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065576-04298180-91b1-11eb-91ce-4505e99ed598.gif[] diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs index 9b1f480446aa..8f3cc86873f5 100644 --- a/crates/ide/src/parent_module.rs +++ b/crates/ide/src/parent_module.rs @@ -18,7 +18,7 @@ use crate::NavigationTarget; // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Locate parent module** +// | VS Code | **rust-analyzer: Locate parent module** // |=== // // image::https://user-images.githubusercontent.com/48062697/113065580-04c21800-91b1-11eb-9a32-00086161c0bd.gif[] diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index bec770ed99f1..b0853b10fde2 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -116,7 +116,7 @@ impl Runnable { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Run** +// | VS Code | **rust-analyzer: Run** // |=== // image::https://user-images.githubusercontent.com/48062697/113065583-055aae80-91b1-11eb-958f-d67efcaf6a2f.gif[] pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { @@ -202,7 +202,7 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Peek related tests** +// | VS Code | **rust-analyzer: Peek related tests** // |=== pub(crate) fn related_tests( db: &RootDatabase, @@ -373,11 +373,13 @@ pub(crate) fn runnable_impl( let adt_name = ty.as_adt()?.name(sema.db); let mut ty_args = ty.type_arguments().peekable(); let params = if ty_args.peek().is_some() { - format!("<{}>", ty_args.format_with(", ", |ty, cb| cb(&ty.display(sema.db)))) + format!("<{}>", ty_args.format_with(",", |ty, cb| cb(&ty.display(sema.db)))) } else { String::new() }; - let test_id = TestId::Path(format!("{}{}", adt_name, params)); + let mut test_id = format!("{}{}", adt_name, params); + test_id.retain(|c| c != ' '); + let test_id = TestId::Path(test_id); Some(Runnable { use_name_in_title: false, nav, kind: RunnableKind::DocTest { test_id }, cfg }) } @@ -441,10 +443,11 @@ fn module_def_doctest(db: &RootDatabase, def: Definition) -> Option { format_to!( path, "<{}>", - ty_args.format_with(", ", |ty, cb| cb(&ty.display(db))) + ty_args.format_with(",", |ty, cb| cb(&ty.display(db))) ); } format_to!(path, "::{}", def_name); + path.retain(|c| c != ' '); return Some(path); } } @@ -2067,13 +2070,23 @@ mod tests { $0 struct Foo; +/// ``` +/// ``` impl Foo { /// ```rust /// ```` fn t() {} } + +/// ``` +/// ``` +impl Foo, ()> { + /// ``` + /// ``` + fn t() {} +} "#, - &[DocTest], + &[DocTest, DocTest, DocTest, DocTest], expect![[r#" [ Runnable { @@ -2082,12 +2095,64 @@ impl Foo { file_id: FileId( 0, ), - full_range: 47..85, + full_range: 20..103, + focus_range: 47..56, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "Foo", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 63..101, + name: "t", + }, + kind: DocTest { + test_id: Path( + "Foo::t", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 105..188, + focus_range: 126..146, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "Foo,()>", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 153..186, name: "t", }, kind: DocTest { test_id: Path( - "Foo::t", + "Foo,()>::t", ), }, cfg: None, diff --git a/crates/ide/src/shuffle_crate_graph.rs b/crates/ide/src/shuffle_crate_graph.rs index 15cb89dcce95..2d86627643d7 100644 --- a/crates/ide/src/shuffle_crate_graph.rs +++ b/crates/ide/src/shuffle_crate_graph.rs @@ -12,7 +12,7 @@ use ide_db::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Shuffle Crate Graph** +// | VS Code | **rust-analyzer: Shuffle Crate Graph** // |=== pub(crate) fn shuffle_crate_graph(db: &mut RootDatabase) { let crate_graph = db.crate_graph(); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index d74b640415c7..cc79ee55b7da 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -204,7 +204,7 @@ impl StaticIndex<'_> { fn get_definition(sema: &Semantics<'_, RootDatabase>, token: SyntaxToken) -> Option { for token in sema.descend_into_macros(token) { - let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions); + let def = IdentClass::classify_token(sema, &token).map(IdentClass::definitions_no_ops); if let Some(&[x]) = def.as_deref() { return Some(x); } else { diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 3191870eb5e8..32e39f82a0e9 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -29,7 +29,7 @@ fn macro_syntax_tree_stats(db: &RootDatabase) -> SyntaxTreeStats { // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Status** +// | VS Code | **rust-analyzer: Status** // |=== // image::https://user-images.githubusercontent.com/48062697/113065584-05f34500-91b1-11eb-98cc-5c196f76be7f.gif[] pub(crate) fn status(db: &RootDatabase, file_id: Option) -> String { diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index 99be7c664868..382735cb368d 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -958,7 +958,7 @@ pub struct Struct; #[test] #[cfg_attr( - all(unix, not(target_pointer_width = "64")), + not(all(unix, target_pointer_width = "64")), ignore = "depends on `DefaultHasher` outputs" )] fn test_rainbow_highlighting() { diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs index 9003e7cd34d0..4256fea0f81e 100644 --- a/crates/ide/src/syntax_tree.rs +++ b/crates/ide/src/syntax_tree.rs @@ -12,7 +12,7 @@ use syntax::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Show Syntax Tree** +// | VS Code | **rust-analyzer: Show Syntax Tree** // |=== // image::https://user-images.githubusercontent.com/48062697/113065586-068bdb80-91b1-11eb-9507-fee67f9f45a0.gif[] pub(crate) fn syntax_tree( diff --git a/crates/ide/src/view_crate_graph.rs b/crates/ide/src/view_crate_graph.rs index 51291a64532f..bf7b7efe2822 100644 --- a/crates/ide/src/view_crate_graph.rs +++ b/crates/ide/src/view_crate_graph.rs @@ -16,7 +16,7 @@ use ide_db::{ // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: View Crate Graph** +// | VS Code | **rust-analyzer: View Crate Graph** // |=== pub(crate) fn view_crate_graph(db: &RootDatabase, full: bool) -> Result { let crate_graph = db.crate_graph(); diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs index 7312afe5310b..bf0835ed7e0d 100644 --- a/crates/ide/src/view_hir.rs +++ b/crates/ide/src/view_hir.rs @@ -8,7 +8,7 @@ use syntax::{algo::find_node_at_offset, ast, AstNode}; // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: View Hir** +// | VS Code | **rust-analyzer: View Hir** // |=== // image::https://user-images.githubusercontent.com/48062697/113065588-068bdb80-91b1-11eb-9a78-0b4ef1e972fb.gif[] pub(crate) fn view_hir(db: &RootDatabase, position: FilePosition) -> String { diff --git a/crates/ide/src/view_item_tree.rs b/crates/ide/src/view_item_tree.rs index 3dc03085d651..9c1f93356ee2 100644 --- a/crates/ide/src/view_item_tree.rs +++ b/crates/ide/src/view_item_tree.rs @@ -9,7 +9,7 @@ use ide_db::RootDatabase; // |=== // | Editor | Action Name // -// | VS Code | **Rust Analyzer: Debug ItemTree** +// | VS Code | **rust-analyzer: Debug ItemTree** // |=== pub(crate) fn view_item_tree(db: &RootDatabase, file_id: FileId) -> String { db.file_item_tree(file_id.into()).pretty_print() diff --git a/crates/paths/src/lib.rs b/crates/paths/src/lib.rs index 025093f4a94a..6ae23ac841ad 100644 --- a/crates/paths/src/lib.rs +++ b/crates/paths/src/lib.rs @@ -106,6 +106,14 @@ impl AsRef for AbsPath { } } +impl ToOwned for AbsPath { + type Owned = AbsPathBuf; + + fn to_owned(&self) -> Self::Owned { + AbsPathBuf(self.0.to_owned()) + } +} + impl<'a> TryFrom<&'a Path> for &'a AbsPath { type Error = &'a Path; fn try_from(path: &'a Path) -> Result<&'a AbsPath, &'a Path> { diff --git a/crates/proc-macro-api/src/lib.rs b/crates/proc-macro-api/src/lib.rs index d7010e825aa9..a3ea05f4aff8 100644 --- a/crates/proc-macro-api/src/lib.rs +++ b/crates/proc-macro-api/src/lib.rs @@ -60,7 +60,7 @@ impl MacroDylib { let info = version::read_dylib_info(&path)?; if info.version.0 < 1 || info.version.1 < 47 { - let msg = format!("proc-macro {} built by {:#?} is not supported by Rust Analyzer, please update your rust version.", path.display(), info); + let msg = format!("proc-macro {} built by {:#?} is not supported by rust-analyzer, please update your Rust version.", path.display(), info); return Err(io::Error::new(io::ErrorKind::InvalidData, msg)); } diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs index bcf3f1184cf6..705d09ea9458 100644 --- a/crates/proc-macro-srv/src/abis/mod.rs +++ b/crates/proc-macro-srv/src/abis/mod.rs @@ -5,7 +5,7 @@ //! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47). //! //! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple -//! interface the rest of rust analyzer can use to talk to the macro +//! interface the rest of rust-analyzer can use to talk to the macro //! provider. //! //! # Adding a new ABI diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 597880c2ca21..eed955b42daa 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -19,7 +19,7 @@ use crate::{utf8_stdout, ManifestPath}; /// [`CargoWorkspace`] represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. /// -/// Note that internally, rust analyzer uses a different structure: +/// Note that internally, rust-analyzer uses a different structure: /// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, /// while this knows about `Packages` & `Targets`: purely cargo-related /// concepts. diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 202a01adf710..09150c77d7dd 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -8,7 +8,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; use crate::lsp_ext; -pub(crate) type CheckFixes = Arc>>; +pub(crate) type CheckFixes = Arc>>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { @@ -22,7 +22,7 @@ pub(crate) struct DiagnosticCollection { // FIXME: should be FxHashMap> pub(crate) native: FxHashMap>, // FIXME: should be Vec - pub(crate) check: FxHashMap>, + pub(crate) check: FxHashMap>>, pub(crate) check_fixes: CheckFixes, changes: FxHashSet, } @@ -35,9 +35,19 @@ pub(crate) struct Fix { } impl DiagnosticCollection { - pub(crate) fn clear_check(&mut self) { + pub(crate) fn clear_check(&mut self, flycheck_id: usize) { + if let Some(it) = Arc::make_mut(&mut self.check_fixes).get_mut(&flycheck_id) { + it.clear(); + } + if let Some(it) = self.check.get_mut(&flycheck_id) { + self.changes.extend(it.drain().map(|(key, _value)| key)); + } + } + + pub(crate) fn clear_check_all(&mut self) { Arc::make_mut(&mut self.check_fixes).clear(); - self.changes.extend(self.check.drain().map(|(key, _value)| key)) + self.changes + .extend(self.check.values_mut().flat_map(|it| it.drain().map(|(key, _value)| key))) } pub(crate) fn clear_native_for(&mut self, file_id: FileId) { @@ -47,11 +57,12 @@ impl DiagnosticCollection { pub(crate) fn add_check_diagnostic( &mut self, + flycheck_id: usize, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option, ) { - let diagnostics = self.check.entry(file_id).or_default(); + let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default(); for existing_diagnostic in diagnostics.iter() { if are_diagnostics_equal(existing_diagnostic, &diagnostic) { return; @@ -59,7 +70,7 @@ impl DiagnosticCollection { } let check_fixes = Arc::make_mut(&mut self.check_fixes); - check_fixes.entry(file_id).or_default().extend(fix); + check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().extend(fix); diagnostics.push(diagnostic); self.changes.insert(file_id); } @@ -89,7 +100,8 @@ impl DiagnosticCollection { file_id: FileId, ) -> impl Iterator { let native = self.native.get(&file_id).into_iter().flatten(); - let check = self.check.get(&file_id).into_iter().flatten(); + let check = + self.check.values().filter_map(move |it| it.get(&file_id)).into_iter().flatten(); native.chain(check) } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 8f881cba4dbd..b5f6aef2e1a8 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; -use ide_db::base_db::{CrateId, FileLoader, SourceDatabase}; +use ide_db::base_db::{CrateId, FileLoader, SourceDatabase, SourceDatabaseExt}; use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; @@ -176,7 +176,7 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - let mut fs_changes = Vec::new(); + let mut fs_refresh_changes = Vec::new(); // A file was added or deleted let mut has_structure_changes = false; @@ -192,15 +192,14 @@ impl GlobalState { if let Some(path) = vfs.file_path(file.file_id).as_path() { let path = path.to_path_buf(); if reload::should_refresh_for_change(&path, file.change_kind) { - self.fetch_workspaces_queue - .request_op(format!("vfs file change: {}", path.display())); + fs_refresh_changes.push((path, file.file_id)); } - fs_changes.push((path, file.change_kind)); if file.is_created_or_deleted() { has_structure_changes = true; } } + // Clear native diagnostics when their file gets deleted if !file.exists() { self.diagnostics.clear_native_for(file.file_id); } @@ -226,14 +225,25 @@ impl GlobalState { self.analysis_host.apply_change(change); - let raw_database = &self.analysis_host.raw_database(); - self.proc_macro_changed = - changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { - let crates = raw_database.relevant_crates(file.file_id); - let crate_graph = raw_database.crate_graph(); + { + let raw_database = self.analysis_host.raw_database(); + let workspace_structure_change = + fs_refresh_changes.into_iter().find(|&(_, file_id)| { + !raw_database.source_root(raw_database.file_source_root(file_id)).is_library + }); + if let Some((path, _)) = workspace_structure_change { + self.fetch_workspaces_queue + .request_op(format!("workspace vfs file change: {}", path.display())); + } + self.proc_macro_changed = + changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { + let crates = raw_database.relevant_crates(file.file_id); + let crate_graph = raw_database.crate_graph(); + + crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) + }); + } - crates.iter().any(|&krate| crate_graph[krate].is_proc_macro) - }); true } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index deb777c952fd..47daa732d5d3 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1094,7 +1094,9 @@ pub(crate) fn handle_code_action( } // Fixes from `cargo check`. - for fix in snap.check_fixes.get(&frange.file_id).into_iter().flatten() { + for fix in + snap.check_fixes.values().filter_map(|it| it.get(&frange.file_id)).into_iter().flatten() + { // FIXME: this mapping is awkward and shouldn't exist. Refactor // `snap.check_fixes` to not convert to LSP prematurely. let intersect_fix_range = fix diff --git a/crates/rust-analyzer/src/integrated_benchmarks.rs b/crates/rust-analyzer/src/integrated_benchmarks.rs index 47cdd8dfc75d..e49a98685a7f 100644 --- a/crates/rust-analyzer/src/integrated_benchmarks.rs +++ b/crates/rust-analyzer/src/integrated_benchmarks.rs @@ -6,8 +6,8 @@ //! code here exercise this specific completion, and thus have a fast //! edit/compile/test cycle. //! -//! Note that "Rust Analyzer: Run" action does not allow running a single test -//! in release mode in VS Code. There's however "Rust Analyzer: Copy Run Command Line" +//! Note that "rust-analyzer: Run" action does not allow running a single test +//! in release mode in VS Code. There's however "rust-analyzer: Copy Run Command Line" //! which you can use to paste the command in terminal and add `--release` manually. use std::sync::Arc; diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 5845cf712c89..b504c2487826 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -2,13 +2,15 @@ //! requests/replies and notifications back to the client. use std::{ fmt, + ops::Deref, sync::Arc, time::{Duration, Instant}, }; use always_assert::always; use crossbeam_channel::{select, Receiver}; -use ide_db::base_db::{SourceDatabaseExt, VfsPath}; +use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath}; +use itertools::Itertools; use lsp_server::{Connection, Notification, Request}; use lsp_types::notification::Notification as _; use vfs::{ChangeKind, FileId}; @@ -371,7 +373,7 @@ impl GlobalState { let _p = profile::span("GlobalState::handle_event/flycheck"); loop { match task { - flycheck::Message::AddDiagnostic { workspace_root, diagnostic } => { + flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { let snap = self.snapshot(); let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( @@ -383,6 +385,7 @@ impl GlobalState { for diag in diagnostics { match url_to_file_id(&self.vfs.read().0, &diag.url) { Ok(file_id) => self.diagnostics.add_check_diagnostic( + id, file_id, diag.diagnostic, diag.fix, @@ -400,7 +403,7 @@ impl GlobalState { flycheck::Message::Progress { id, progress } => { let (state, message) = match progress { flycheck::Progress::DidStart => { - self.diagnostics.clear_check(); + self.diagnostics.clear_check(id); (Progress::Begin, None) } flycheck::Progress::DidCheckCrate(target) => { @@ -444,7 +447,10 @@ impl GlobalState { let memdocs_added_or_removed = self.mem_docs.take_changes(); if self.is_quiescent() { - if !was_quiescent { + if !was_quiescent + && !self.fetch_workspaces_queue.op_requested() + && !self.fetch_build_data_queue.op_requested() + { for flycheck in &self.flycheck { flycheck.update(); } @@ -734,13 +740,76 @@ impl GlobalState { Ok(()) })? .on::(|this, params| { - for flycheck in &this.flycheck { - flycheck.update(); + let mut updated = false; + if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { + let (vfs, _) = &*this.vfs.read(); + if let Some(file_id) = vfs.file_id(&vfs_path) { + let analysis = this.analysis_host.analysis(); + // Crates containing or depending on the saved file + let crate_ids: Vec<_> = analysis + .crate_for(file_id)? + .into_iter() + .flat_map(|id| { + this.analysis_host + .raw_database() + .crate_graph() + .transitive_rev_deps(id) + }) + .sorted() + .unique() + .collect(); + + let crate_root_paths: Vec<_> = crate_ids + .iter() + .filter_map(|&crate_id| { + analysis + .crate_root(crate_id) + .map(|file_id| { + vfs.file_path(file_id).as_path().map(ToOwned::to_owned) + }) + .transpose() + }) + .collect::>()?; + let crate_root_paths: Vec<_> = + crate_root_paths.iter().map(Deref::deref).collect(); + + // Find all workspaces that have at least one target containing the saved file + let workspace_ids = + this.workspaces.iter().enumerate().filter(|(_, ws)| match ws { + project_model::ProjectWorkspace::Cargo { cargo, .. } => { + cargo.packages().any(|pkg| { + cargo[pkg].targets.iter().any(|&it| { + crate_root_paths.contains(&cargo[it].root.as_path()) + }) + }) + } + project_model::ProjectWorkspace::Json { project, .. } => project + .crates() + .any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)), + project_model::ProjectWorkspace::DetachedFiles { .. } => false, + }); + + // Find and trigger corresponding flychecks + for flycheck in &this.flycheck { + for (id, _) in workspace_ids.clone() { + if id == flycheck.id() { + updated = true; + flycheck.update(); + continue; + } + } + } + } + if let Some(abs_path) = vfs_path.as_path() { + if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) { + this.fetch_workspaces_queue + .request_op(format!("DidSaveTextDocument {}", abs_path.display())); + } + } } - if let Ok(abs_path) = from_proto::abs_path(¶ms.text_document.uri) { - if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) { - this.fetch_workspaces_queue - .request_op(format!("DidSaveTextDocument {}", abs_path.display())); + if !updated { + for flycheck in &this.flycheck { + flycheck.update(); } } Ok(()) diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index eaab275bc68a..49ccad71a10e 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -196,10 +196,7 @@ impl GlobalState { } if let Err(error) = self.fetch_build_data_error() { - self.show_and_log_error( - "rust-analyzer failed to run build scripts".to_string(), - Some(error), - ); + self.show_and_log_error("failed to run build scripts".to_string(), Some(error)); } let workspaces = self @@ -308,6 +305,7 @@ impl GlobalState { if self.proc_macro_clients.is_empty() { if let Some((path, args)) = self.config.proc_macro_srv() { + tracing::info!("Spawning proc-macro servers"); self.proc_macro_clients = self .workspaces .iter() @@ -316,20 +314,20 @@ impl GlobalState { let mut path = path.clone(); if let ProjectWorkspace::Cargo { sysroot, .. } = ws { - tracing::info!("Found a cargo workspace..."); + tracing::debug!("Found a cargo workspace..."); if let Some(sysroot) = sysroot.as_ref() { - tracing::info!("Found a cargo workspace with a sysroot..."); + tracing::debug!("Found a cargo workspace with a sysroot..."); let server_path = sysroot.root().join("libexec").join(&standalone_server_name); if std::fs::metadata(&server_path).is_ok() { - tracing::info!( + tracing::debug!( "And the server exists at {}", server_path.display() ); path = server_path; args = vec![]; } else { - tracing::info!( + tracing::debug!( "And the server does not exist at {}", server_path.display() ); @@ -337,14 +335,10 @@ impl GlobalState { } } - tracing::info!( - "Using proc-macro server at {} with args {:?}", - path.display(), - args - ); + tracing::info!(?args, "Using proc-macro server at {}", path.display(),); ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|err| { let error = format!( - "Failed to run proc_macro_srv from path {}, error: {:?}", + "Failed to run proc-macro server from path {}, error: {:?}", path.display(), err ); @@ -458,7 +452,7 @@ impl GlobalState { Some(it) => it, None => { self.flycheck = Vec::new(); - self.diagnostics.clear_check(); + self.diagnostics.clear_check_all(); return; } }; @@ -621,7 +615,10 @@ pub(crate) fn load_proc_macro( }; let expander: Arc = if dummy_replace.iter().any(|replace| &**replace == name) { - Arc::new(DummyExpander) + match kind { + ProcMacroKind::Attr => Arc::new(IdentityExpander), + _ => Arc::new(EmptyExpander), + } } else { Arc::new(Expander(expander)) }; @@ -647,11 +644,11 @@ pub(crate) fn load_proc_macro( } } - /// Dummy identity expander, used for proc-macros that are deliberately ignored by the user. + /// Dummy identity expander, used for attribute proc-macros that are deliberately ignored by the user. #[derive(Debug)] - struct DummyExpander; + struct IdentityExpander; - impl ProcMacroExpander for DummyExpander { + impl ProcMacroExpander for IdentityExpander { fn expand( &self, subtree: &tt::Subtree, @@ -661,27 +658,46 @@ pub(crate) fn load_proc_macro( Ok(subtree.clone()) } } + + /// Empty expander, used for proc-macros that are deliberately ignored by the user. + #[derive(Debug)] + struct EmptyExpander; + + impl ProcMacroExpander for EmptyExpander { + fn expand( + &self, + _: &tt::Subtree, + _: Option<&tt::Subtree>, + _: &Env, + ) -> Result { + Ok(tt::Subtree::default()) + } + } } pub(crate) fn should_refresh_for_change(path: &AbsPath, change_kind: ChangeKind) -> bool { const IMPLICIT_TARGET_FILES: &[&str] = &["build.rs", "src/main.rs", "src/lib.rs"]; const IMPLICIT_TARGET_DIRS: &[&str] = &["src/bin", "examples", "tests", "benches"]; - let file_name = path.file_name().unwrap_or_default(); - if file_name == "Cargo.toml" || file_name == "Cargo.lock" { + let file_name = match path.file_name().unwrap_or_default().to_str() { + Some(it) => it, + None => return false, + }; + + if let "Cargo.toml" | "Cargo.lock" = file_name { return true; } if change_kind == ChangeKind::Modify { return false; } + + // .cargo/config{.toml} if path.extension().unwrap_or_default() != "rs" { - if (file_name == "config.toml" || file_name == "config") - && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")) == Some(true) - { - return true; - } - return false; + let is_cargo_config = matches!(file_name, "config.toml" | "config") + && path.parent().map(|parent| parent.as_ref().ends_with(".cargo")).unwrap_or(false); + return is_cargo_config; } + if IMPLICIT_TARGET_FILES.iter().any(|it| path.as_ref().ends_with(it)) { return true; } diff --git a/crates/rust-analyzer/tests/slow-tests/tidy.rs b/crates/rust-analyzer/tests/slow-tests/tidy.rs index 18f95925d9a4..58099a58de05 100644 --- a/crates/rust-analyzer/tests/slow-tests/tidy.rs +++ b/crates/rust-analyzer/tests/slow-tests/tidy.rs @@ -13,9 +13,8 @@ use xshell::cmd; fn check_code_formatting() { let sh = &Shell::new().unwrap(); sh.change_dir(sourcegen::project_root()); - sh.set_var("RUSTUP_TOOLCHAIN", "stable"); - let out = cmd!(sh, "rustfmt --version").read().unwrap(); + let out = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap(); if !out.contains("stable") { panic!( "Failed to run rustfmt from toolchain 'stable'. \ @@ -23,9 +22,9 @@ fn check_code_formatting() { ) } - let res = cmd!(sh, "cargo fmt -- --check").run(); + let res = cmd!(sh, "rustup run stable cargo fmt -- --check").run(); if res.is_err() { - let _ = cmd!(sh, "cargo fmt").run(); + let _ = cmd!(sh, "rustup run stable cargo fmt").run(); } res.unwrap() } diff --git a/crates/sourcegen/src/lib.rs b/crates/sourcegen/src/lib.rs index ce0224ec744d..4e0ee63f32f2 100644 --- a/crates/sourcegen/src/lib.rs +++ b/crates/sourcegen/src/lib.rs @@ -136,7 +136,7 @@ impl fmt::Display for Location { } fn ensure_rustfmt(sh: &Shell) { - let version = cmd!(sh, "rustfmt --version").read().unwrap_or_default(); + let version = cmd!(sh, "rustup run stable rustfmt --version").read().unwrap_or_default(); if !version.contains("stable") { panic!( "Failed to run rustfmt from toolchain 'stable'. \ @@ -147,13 +147,15 @@ fn ensure_rustfmt(sh: &Shell) { pub fn reformat(text: String) -> String { let sh = Shell::new().unwrap(); - sh.set_var("RUSTUP_TOOLCHAIN", "stable"); ensure_rustfmt(&sh); let rustfmt_toml = project_root().join("rustfmt.toml"); - let mut stdout = cmd!(sh, "rustfmt --config-path {rustfmt_toml} --config fn_single_line=true") - .stdin(text) - .read() - .unwrap(); + let mut stdout = cmd!( + sh, + "rustup run stable rustfmt --config-path {rustfmt_toml} --config fn_single_line=true" + ) + .stdin(text) + .read() + .unwrap(); if !stdout.ends_with('\n') { stdout.push('\n'); } diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs index e3e928aecd4a..8efd58e2c39a 100644 --- a/crates/syntax/src/ast/edit_in_place.rs +++ b/crates/syntax/src/ast/edit_in_place.rs @@ -11,7 +11,7 @@ use crate::{ ted::{self, Position}, AstNode, AstToken, Direction, SyntaxKind::{ATTR, COMMENT, WHITESPACE}, - SyntaxNode, + SyntaxNode, SyntaxToken, }; use super::HasName; @@ -506,19 +506,7 @@ impl ast::RecordExprFieldList { let position = match self.fields().last() { Some(last_field) => { - let comma = match last_field - .syntax() - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { - Some(it) => it, - None => { - let comma = ast::make::token(T![,]); - ted::insert(Position::after(last_field.syntax()), &comma); - comma - } - }; + let comma = get_or_insert_comma_after(last_field.syntax()); Position::after(comma) } None => match self.l_curly_token() { @@ -579,19 +567,8 @@ impl ast::RecordPatFieldList { let position = match self.fields().last() { Some(last_field) => { - let comma = match last_field - .syntax() - .siblings_with_tokens(Direction::Next) - .filter_map(|it| it.into_token()) - .find(|it| it.kind() == T![,]) - { - Some(it) => it, - None => { - let comma = ast::make::token(T![,]); - ted::insert(Position::after(last_field.syntax()), &comma); - comma - } - }; + let syntax = last_field.syntax(); + let comma = get_or_insert_comma_after(syntax); Position::after(comma) } None => match self.l_curly_token() { @@ -606,12 +583,53 @@ impl ast::RecordPatFieldList { } } } + +fn get_or_insert_comma_after(syntax: &SyntaxNode) -> SyntaxToken { + let comma = match syntax + .siblings_with_tokens(Direction::Next) + .filter_map(|it| it.into_token()) + .find(|it| it.kind() == T![,]) + { + Some(it) => it, + None => { + let comma = ast::make::token(T![,]); + ted::insert(Position::after(syntax), &comma); + comma + } + }; + comma +} + impl ast::StmtList { pub fn push_front(&self, statement: ast::Stmt) { ted::insert(Position::after(self.l_curly_token().unwrap()), statement.syntax()); } } +impl ast::VariantList { + pub fn add_variant(&self, variant: ast::Variant) { + let (indent, position) = match self.variants().last() { + Some(last_item) => ( + IndentLevel::from_node(last_item.syntax()), + Position::after(get_or_insert_comma_after(last_item.syntax())), + ), + None => match self.l_curly_token() { + Some(l_curly) => { + normalize_ws_between_braces(self.syntax()); + (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly)) + } + None => (IndentLevel::single(), Position::last_child_of(self.syntax())), + }, + }; + let elements: Vec> = vec![ + make::tokens::whitespace(&format!("{}{}", "\n", indent)).into(), + variant.syntax().clone().into(), + ast::make::token(T![,]).into(), + ]; + ted::insert_all(position, elements); + } +} + fn normalize_ws_between_braces(node: &SyntaxNode) -> Option<()> { let l = node .children_with_tokens() @@ -661,6 +679,9 @@ impl Indent for N {} mod tests { use std::fmt; + use stdx::trim_indent; + use test_utils::assert_eq_text; + use crate::SourceFile; use super::*; @@ -714,4 +735,100 @@ mod tests { }", ); } + + #[test] + fn add_variant_to_empty_enum() { + let variant = make::variant(make::name("Bar"), None).clone_for_update(); + + check_add_variant( + r#" +enum Foo {} +"#, + r#" +enum Foo { + Bar, +} +"#, + variant, + ); + } + + #[test] + fn add_variant_to_non_empty_enum() { + let variant = make::variant(make::name("Baz"), None).clone_for_update(); + + check_add_variant( + r#" +enum Foo { + Bar, +} +"#, + r#" +enum Foo { + Bar, + Baz, +} +"#, + variant, + ); + } + + #[test] + fn add_variant_with_tuple_field_list() { + let variant = make::variant( + make::name("Baz"), + Some(ast::FieldList::TupleFieldList(make::tuple_field_list(std::iter::once( + make::tuple_field(None, make::ty("bool")), + )))), + ) + .clone_for_update(); + + check_add_variant( + r#" +enum Foo { + Bar, +} +"#, + r#" +enum Foo { + Bar, + Baz(bool), +} +"#, + variant, + ); + } + + #[test] + fn add_variant_with_record_field_list() { + let variant = make::variant( + make::name("Baz"), + Some(ast::FieldList::RecordFieldList(make::record_field_list(std::iter::once( + make::record_field(None, make::name("x"), make::ty("bool")), + )))), + ) + .clone_for_update(); + + check_add_variant( + r#" +enum Foo { + Bar, +} +"#, + r#" +enum Foo { + Bar, + Baz { x: bool }, +} +"#, + variant, + ); + } + + fn check_add_variant(before: &str, expected: &str, variant: ast::Variant) { + let enum_ = ast_mut_from_text::(before); + enum_.variant_list().map(|it| it.add_variant(variant)); + let after = enum_.to_string(); + assert_eq_text!(&trim_indent(expected.trim()), &trim_indent(&after.trim())); + } } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 5908dda8e638..037de876d45c 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -745,7 +745,10 @@ pub fn tuple_field(visibility: Option, ty: ast::Type) -> ast::T pub fn variant(name: ast::Name, field_list: Option) -> ast::Variant { let field_list = match field_list { None => String::new(), - Some(it) => format!("{}", it), + Some(it) => match it { + ast::FieldList::RecordFieldList(record) => format!(" {}", record), + ast::FieldList::TupleFieldList(tuple) => format!("{}", tuple), + }, }; ast_from_text(&format!("enum f {{ {}{} }}", name, field_list)) } diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index 7fa354c0c465..4f5e273a520a 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -1,4 +1,4 @@ -//! Syntax Tree library used throughout the rust analyzer. +//! Syntax Tree library used throughout the rust-analyzer. //! //! Properties: //! - easy and fast incremental re-parsing diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index 4d33a9afb963..d6d9c66159fe 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -40,12 +40,15 @@ impl loader::Handle for NotifyHandle { .expect("failed to spawn thread"); NotifyHandle { sender, _thread: thread } } + fn set_config(&mut self, config: loader::Config) { self.sender.send(Message::Config(config)).unwrap(); } + fn invalidate(&mut self, path: AbsPathBuf) { self.sender.send(Message::Invalidate(path)).unwrap(); } + fn load_sync(&mut self, path: &AbsPath) -> Option> { read(path) } @@ -70,6 +73,7 @@ impl NotifyActor { fn new(sender: loader::Sender) -> NotifyActor { NotifyActor { sender, watched_entries: Vec::new(), watcher: None } } + fn next_event(&self, receiver: &Receiver) -> Option { let watcher_receiver = self.watcher.as_ref().map(|(_, receiver)| receiver); select! { @@ -77,9 +81,10 @@ impl NotifyActor { recv(watcher_receiver.unwrap_or(&never())) -> it => Some(Event::NotifyEvent(it.unwrap())), } } + fn run(mut self, inbox: Receiver) { while let Some(event) = self.next_event(&inbox) { - tracing::debug!("vfs-notify event: {:?}", event); + tracing::debug!(?event, "vfs-notify event"); match event { Event::Message(msg) => match msg { Message::Config(config) => { diff --git a/docs/dev/README.md b/docs/dev/README.md index 76bbd1e91889..c7f152acc266 100644 --- a/docs/dev/README.md +++ b/docs/dev/README.md @@ -82,7 +82,7 @@ There's **"Run Extension (Debug Build)"** launch configuration for this in VS Co In general, I use one of the following workflows for fixing bugs and implementing features: If the problem concerns only internal parts of rust-analyzer (i.e. I don't need to touch the `rust-analyzer` crate or TypeScript code), there is a unit-test for it. -So, I use **Rust Analyzer: Run** action in VS Code to run this single test, and then just do printf-driven development/debugging. +So, I use **rust-analyzer: Run** action in VS Code to run this single test, and then just do printf-driven development/debugging. As a sanity check after I'm done, I use `cargo xtask install --server` and **Reload Window** action in VS Code to verify that the thing works as I expect. If the problem concerns only the VS Code extension, I use **Run Installed Extension** launch configuration from `launch.json`. @@ -152,11 +152,11 @@ To log all communication between the server and the client, there are two choice There are also several VS Code commands which might be of interest: -* `Rust Analyzer: Status` shows some memory-usage statistics. +* `rust-analyzer: Status` shows some memory-usage statistics. -* `Rust Analyzer: Syntax Tree` shows syntax tree of the current file/selection. +* `rust-analyzer: Syntax Tree` shows syntax tree of the current file/selection. -* `Rust Analyzer: View Hir` shows the HIR expressions within the function containing the cursor. +* `rust-analyzer: View Hir` shows the HIR expressions within the function containing the cursor. You can hover over syntax nodes in the opened text file to see the appropriate rust code that it refers to and the rust editor will also highlight the proper diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index ea4035baf114..51e26c58a917 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -371,7 +371,7 @@ That is, rust-analyzer requires unwinding. ### Testing -Rust Analyzer has three interesting [system boundaries](https://www.tedinski.com/2018/04/10/making-tests-a-positive-influence-on-design.html) to concentrate tests on. +rust-analyzer has three interesting [system boundaries](https://www.tedinski.com/2018/04/10/making-tests-a-positive-influence-on-design.html) to concentrate tests on. The outermost boundary is the `rust-analyzer` crate, which defines an LSP interface in terms of stdio. We do integration testing of this component, by feeding it with a stream of LSP requests and checking responses. diff --git a/docs/dev/guide.md b/docs/dev/guide.md index 47ae3f3e6a90..808eb5d10bf4 100644 --- a/docs/dev/guide.md +++ b/docs/dev/guide.md @@ -63,7 +63,7 @@ Next, let's talk about what the inputs to the `Analysis` are, precisely. ## Inputs -Rust Analyzer never does any I/O itself, all inputs get passed explicitly via +rust-analyzer never does any I/O itself, all inputs get passed explicitly via the `AnalysisHost::apply_change` method, which accepts a single argument, a `Change`. [`Change`] is a builder for a single change "transaction", so it suffices to study its methods to understand all of the diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 999a6437ab9e..c482fcbed0e0 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -479,7 +479,7 @@ You can follow instructions for installing < `${this.rootSection}.${opt}` - ); + private readonly requiresWorkspaceReloadOpts = [ + "serverPath", + "server", + // FIXME: This shouldn't be here, changing this setting should reload + // `continueCommentsOnNewline` behavior without restart + "typing", + ].map((opt) => `${this.rootSection}.${opt}`); private readonly requiresReloadOpts = [ "cargo", "procMacro", @@ -140,6 +144,10 @@ export class Config { return this.get("restartServerOnConfigChange"); } + get typingContinueCommentsOnNewline() { + return this.get("typing.continueCommentsOnNewline"); + } + get debug() { let sourceFileMap = this.get | "auto">("debug.sourceFileMap"); if (sourceFileMap !== "auto") { diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 9ae20ddc4ac4..d78b711a47a8 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -84,7 +84,9 @@ async function tryActivate(context: vscode.ExtensionContext): Promise diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs index dadee43b1085..a3fe59e946ee 100644 --- a/lib/la-arena/src/lib.rs +++ b/lib/la-arena/src/lib.rs @@ -12,7 +12,7 @@ use std::{ }; mod map; -pub use map::ArenaMap; +pub use map::{ArenaMap, Entry, OccupiedEntry, VacantEntry}; /// The raw index of a value in an arena. #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -208,6 +208,16 @@ impl Arena { Arena { data: Vec::new() } } + /// Create a new empty arena with specific capacity. + /// + /// ``` + /// let arena: la_arena::Arena = la_arena::Arena::with_capacity(42); + /// assert!(arena.is_empty()); + /// ``` + pub fn with_capacity(capacity: usize) -> Arena { + Arena { data: Vec::with_capacity(capacity) } + } + /// Empties the arena, removing all contained values. /// /// ``` diff --git a/lib/la-arena/src/map.rs b/lib/la-arena/src/map.rs index d27f086d37bf..5f347e274500 100644 --- a/lib/la-arena/src/map.rs +++ b/lib/la-arena/src/map.rs @@ -11,12 +11,52 @@ pub struct ArenaMap { } impl ArenaMap, V> { + /// Creates a new empty map. + pub const fn new() -> Self { + Self { v: Vec::new(), _ty: PhantomData } + } + + /// Create a new empty map with specific capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self { v: Vec::with_capacity(capacity), _ty: PhantomData } + } + + /// Reserves capacity for at least additional more elements to be inserted in the map. + pub fn reserve(&mut self, additional: usize) { + self.v.reserve(additional); + } + + /// Clears the map, removing all elements. + pub fn clear(&mut self) { + self.v.clear(); + } + + /// Shrinks the capacity of the map as much as possible. + pub fn shrink_to_fit(&mut self) { + let min_len = self.v.iter().rposition(|slot| slot.is_some()).map_or(0, |i| i + 1); + self.v.truncate(min_len); + self.v.shrink_to_fit(); + } + + /// Returns whether the map contains a value for the specified index. + pub fn contains_idx(&self, idx: Idx) -> bool { + matches!(self.v.get(Self::to_idx(idx)), Some(Some(_))) + } + + /// Removes an index from the map, returning the value at the index if the index was previously in the map. + pub fn remove(&mut self, idx: Idx) -> Option { + self.v.get_mut(Self::to_idx(idx))?.take() + } + /// Inserts a value associated with a given arena index into the map. - pub fn insert(&mut self, idx: Idx, t: V) { + /// + /// If the map did not have this index present, None is returned. + /// Otherwise, the value is updated, and the old value is returned. + pub fn insert(&mut self, idx: Idx, t: V) -> Option { let idx = Self::to_idx(idx); self.v.resize_with((idx + 1).max(self.v.len()), || None); - self.v[idx] = Some(t); + self.v[idx].replace(t) } /// Returns a reference to the value associated with the provided index @@ -46,6 +86,16 @@ impl ArenaMap, V> { self.v.iter().enumerate().filter_map(|(idx, o)| Some((Self::from_idx(idx), o.as_ref()?))) } + /// Gets the given key's corresponding entry in the map for in-place manipulation. + pub fn entry(&mut self, idx: Idx) -> Entry<'_, Idx, V> { + let idx = Self::to_idx(idx); + self.v.resize_with((idx + 1).max(self.v.len()), || None); + match &mut self.v[idx] { + slot @ Some(_) => Entry::Occupied(OccupiedEntry { slot, _ty: PhantomData }), + slot @ None => Entry::Vacant(VacantEntry { slot, _ty: PhantomData }), + } + } + fn to_idx(idx: Idx) -> usize { u32::from(idx.into_raw()) as usize } @@ -70,6 +120,119 @@ impl std::ops::IndexMut> for ArenaMap, T> { impl Default for ArenaMap, T> { fn default() -> Self { - ArenaMap { v: Vec::new(), _ty: PhantomData } + Self::new() + } +} + +impl Extend<(Idx, T)> for ArenaMap, T> { + fn extend, T)>>(&mut self, iter: I) { + iter.into_iter().for_each(move |(k, v)| { + self.insert(k, v); + }); + } +} + +impl FromIterator<(Idx, T)> for ArenaMap, T> { + fn from_iter, T)>>(iter: I) -> Self { + let mut this = Self::new(); + this.extend(iter); + this + } +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`ArenaMap`]. +/// +/// [`entry`]: ArenaMap::entry +pub enum Entry<'a, IDX, V> { + /// A vacant entry. + Vacant(VacantEntry<'a, IDX, V>), + /// An occupied entry. + Occupied(OccupiedEntry<'a, IDX, V>), +} + +impl<'a, IDX, V> Entry<'a, IDX, V> { + /// Ensures a value is in the entry by inserting the default if empty, and returns a mutable reference to + /// the value in the entry. + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Self::Vacant(ent) => ent.insert(default), + Self::Occupied(ent) => ent.into_mut(), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns + /// a mutable reference to the value in the entry. + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Self::Vacant(ent) => ent.insert(default()), + Self::Occupied(ent) => ent.into_mut(), + } + } + + /// Provides in-place mutable access to an occupied entry before any potential inserts into the map. + pub fn and_modify(mut self, f: F) -> Self { + if let Self::Occupied(ent) = &mut self { + f(ent.get_mut()); + } + self + } +} + +impl<'a, IDX, V> Entry<'a, IDX, V> +where + V: Default, +{ + /// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference + /// to the value in the entry. + pub fn or_default(self) -> &'a mut V { + self.or_insert_with(Default::default) + } +} + +/// A view into an vacant entry in a [`ArenaMap`]. It is part of the [`Entry`] enum. +pub struct VacantEntry<'a, IDX, V> { + slot: &'a mut Option, + _ty: PhantomData, +} + +impl<'a, IDX, V> VacantEntry<'a, IDX, V> { + /// Sets the value of the entry with the `VacantEntry`’s key, and returns a mutable reference to it. + pub fn insert(self, value: V) -> &'a mut V { + self.slot.insert(value) + } +} + +/// A view into an occupied entry in a [`ArenaMap`]. It is part of the [`Entry`] enum. +pub struct OccupiedEntry<'a, IDX, V> { + slot: &'a mut Option, + _ty: PhantomData, +} + +impl<'a, IDX, V> OccupiedEntry<'a, IDX, V> { + /// Gets a reference to the value in the entry. + pub fn get(&self) -> &V { + self.slot.as_ref().expect("Occupied") + } + + /// Gets a mutable reference to the value in the entry. + pub fn get_mut(&mut self) -> &mut V { + self.slot.as_mut().expect("Occupied") + } + + /// Converts the entry into a mutable reference to its value. + pub fn into_mut(self) -> &'a mut V { + self.slot.as_mut().expect("Occupied") + } + + /// Sets the value of the entry with the `OccupiedEntry`’s key, and returns the entry’s old value. + pub fn insert(&mut self, value: V) -> V { + self.slot.replace(value).expect("Occupied") + } + + /// Takes the value of the entry out of the map, and returns it. + pub fn remove(self) -> V { + self.slot.take().expect("Occupied") } } diff --git a/xtask/src/release.rs b/xtask/src/release.rs index 17ada5156407..eda8fceef05b 100644 --- a/xtask/src/release.rs +++ b/xtask/src/release.rs @@ -81,7 +81,7 @@ impl flags::Promote { let date = date_iso(sh)?; let branch = format!("rust-analyzer-{date}"); cmd!(sh, "git switch -c {branch}").run()?; - cmd!(sh, "git subtree pull -P src/tools/rust-analyzer rust-analyzer master").run()?; + cmd!(sh, "git subtree pull -m ':arrow_up: rust-analyzer' -P src/tools/rust-analyzer rust-analyzer release").run()?; if !self.dry_run { cmd!(sh, "git push -u origin {branch}").run()?; From 8231fee466a7a288a3b506a5fdf0ab057b2acbeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 16 Aug 2022 11:24:50 +0300 Subject: [PATCH 05/22] :arrow_up: rust-analyzer --- .github/workflows/ci.yaml | 2 +- .github/workflows/release.yaml | 10 +- .vscode/launch.json | 2 +- Cargo.lock | 1 + crates/base-db/src/input.rs | 2 +- .../macro_expansion_tests/builtin_fn_macro.rs | 4 +- .../hir-def/src/macro_expansion_tests/mbe.rs | 12 +- .../macro_expansion_tests/mbe/regression.rs | 4 +- .../mbe/tt_conversion.rs | 2 +- .../src/macro_expansion_tests/proc_macros.rs | 2 +- crates/hir-def/src/nameres/mod_resolution.rs | 2 + .../src/nameres/tests/mod_resolution.rs | 4 +- crates/hir-expand/src/builtin_fn_macro.rs | 22 +- crates/hir-expand/src/fixup.rs | 190 +- crates/hir-expand/src/mod_path.rs | 16 +- crates/hir-expand/src/name.rs | 52 +- crates/hir-expand/src/quote.rs | 4 +- crates/hir-ty/src/consteval.rs | 1 - crates/hir-ty/src/infer/pat.rs | 33 +- crates/hir-ty/src/tests/patterns.rs | 45 + crates/hir/src/diagnostics.rs | 1 + crates/hir/src/source_analyzer.rs | 1 + crates/ide-assists/src/assist_context.rs | 157 +- .../src/handlers/add_missing_impl_members.rs | 2 +- .../convert_tuple_struct_to_named_struct.rs | 10 +- .../src/handlers/destructure_tuple_binding.rs | 10 +- .../extract_struct_from_enum_variant.rs | 4 +- .../src/handlers/generate_deref.rs | 4 +- .../src/handlers/introduce_named_lifetime.rs | 4 +- .../src/handlers/remove_unused_param.rs | 5 +- .../replace_derive_with_manual_impl.rs | 4 +- crates/ide-assists/src/utils.rs | 4 +- crates/ide-completion/src/completions.rs | 1 - crates/ide-completion/src/completions/expr.rs | 112 +- .../src/completions/item_list/trait_impl.rs | 3 +- .../ide-completion/src/completions/record.rs | 59 +- crates/ide-completion/src/context.rs | 1 + crates/ide-completion/src/render.rs | 11 +- crates/ide-completion/src/render/const_.rs | 2 +- crates/ide-completion/src/render/function.rs | 8 +- crates/ide-completion/src/render/literal.rs | 2 +- crates/ide-completion/src/render/macro_.rs | 2 +- crates/ide-completion/src/render/pattern.rs | 10 +- .../ide-completion/src/render/type_alias.rs | 4 +- .../src/render/union_literal.rs | 12 +- crates/ide-completion/src/render/variant.rs | 4 +- crates/ide-completion/src/tests/record.rs | 54 +- crates/ide-db/src/search.rs | 2 +- crates/ide-db/src/source_change.rs | 136 +- crates/ide-diagnostics/Cargo.toml | 1 + .../src/handlers/inactive_code.rs | 2 +- .../src/handlers/json_is_not_rust.rs | 310 ++ .../src/handlers/macro_error.rs | 2 +- .../src/handlers/no_such_field.rs | 18 +- crates/ide-diagnostics/src/lib.rs | 33 +- crates/ide-diagnostics/src/tests.rs | 12 +- crates/ide/src/goto_definition.rs | 2 +- crates/ide/src/runnables.rs | 186 + crates/ide/src/status.rs | 2 +- crates/mbe/src/syntax_bridge.rs | 11 +- crates/parser/src/grammar/expressions.rs | 26 +- crates/parser/src/grammar/patterns.rs | 34 +- ...ord_literal_missing_ellipsis_recovery.rast | 43 + ...ecord_literal_missing_ellipsis_recovery.rs | 3 + .../parser/inline/ok/0058_range_pat.rast | 116 + .../parser/inline/ok/0058_range_pat.rs | 10 + .../parser/inline/ok/0061_record_lit.rast | 49 + .../parser/inline/ok/0061_record_lit.rs | 2 + .../inline/ok/0166_half_open_range_pat.rast | 23 +- .../inline/ok/0166_half_open_range_pat.rs | 5 +- crates/proc-macro-api/src/msg/flat.rs | 5 +- .../src/abis/abi_1_58/proc_macro/mod.rs | 4 +- .../src/abis/abi_1_58/ra_server.rs | 1 - .../src/abis/abi_1_63/proc_macro/mod.rs | 4 +- .../src/abis/abi_1_63/ra_server.rs | 1 - .../proc-macro-srv/src/abis/abi_1_64/mod.rs | 105 - .../abis/abi_1_64/proc_macro/bridge/buffer.rs | 156 - .../abis/abi_1_64/proc_macro/bridge/client.rs | 529 --- .../abi_1_64/proc_macro/bridge/closure.rs | 32 - .../abis/abi_1_64/proc_macro/bridge/handle.rs | 89 - .../abis/abi_1_64/proc_macro/bridge/mod.rs | 493 -- .../abis/abi_1_64/proc_macro/bridge/rpc.rs | 304 -- .../abi_1_64/proc_macro/bridge/scoped_cell.rs | 81 - .../proc_macro/bridge/selfless_reify.rs | 84 - .../abis/abi_1_64/proc_macro/bridge/server.rs | 339 -- .../abis/abi_1_64/proc_macro/diagnostic.rs | 166 - .../src/abis/abi_1_64/proc_macro/mod.rs | 1125 ----- .../src/abis/abi_1_64/proc_macro/quote.rs | 139 - .../src/abis/abi_1_64/ra_server.rs | 792 ---- .../src/abis/abi_sysroot/ra_server.rs | 2 +- crates/proc-macro-srv/src/abis/mod.rs | 11 +- crates/proc-macro-srv/src/dylib.rs | 1 - crates/proc-macro-srv/src/tests/mod.rs | 6 +- crates/project-model/src/build_scripts.rs | 26 +- crates/project-model/src/tests.rs | 1 + crates/project-model/src/workspace.rs | 18 +- crates/rust-analyzer/src/bin/rustc_wrapper.rs | 7 +- crates/rust-analyzer/src/cli/diagnostics.rs | 2 +- crates/rust-analyzer/src/cli/flags.rs | 6 +- crates/rust-analyzer/src/config.rs | 1 + .../rust-analyzer/src/diagnostics/to_proto.rs | 2 +- crates/rust-analyzer/src/handlers.rs | 3 +- crates/rust-analyzer/src/main_loop.rs | 399 +- crates/rust-analyzer/src/reload.rs | 1 + crates/rust-analyzer/src/to_proto.rs | 2 +- crates/syntax/src/ast/operators.rs | 2 +- crates/syntax/src/fuzz.rs | 5 +- editors/code/package-lock.json | 3974 ++++++++++++++++- editors/code/src/client.ts | 16 + editors/code/src/commands.ts | 18 +- lib/la-arena/src/lib.rs | 1 - 111 files changed, 5774 insertions(+), 5108 deletions(-) create mode 100644 crates/ide-diagnostics/src/handlers/json_is_not_rust.rs create mode 100644 crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast create mode 100644 crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/mod.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/buffer.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/client.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/closure.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/handle.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/mod.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/rpc.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/scoped_cell.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/selfless_reify.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/server.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/diagnostic.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/mod.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/quote.rs delete mode 100644 crates/proc-macro-srv/src/abis/abi_1_64/ra_server.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 0c81ff0789fb..a70252fa65a5 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -101,7 +101,7 @@ jobs: - name: Install Nodejs uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 16.x - name: Install xvfb if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index ca8eb1309de3..3c36c4fb84a2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -68,7 +68,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 16.x - name: Update apt repositories if: matrix.target == 'aarch64-unknown-linux-gnu' || matrix.target == 'arm-unknown-linux-gnueabihf' @@ -133,7 +133,7 @@ jobs: container: image: rust:alpine volumes: - - /usr/local/cargo/registry + - /usr/local/cargo/registry:/usr/local/cargo/registry steps: - name: Install dependencies @@ -176,7 +176,7 @@ jobs: - name: Install Nodejs uses: actions/setup-node@v1 with: - node-version: 14.x + node-version: 16.x - run: echo "TAG=$(date --iso -u)" >> $GITHUB_ENV if: github.ref == 'refs/heads/release' @@ -253,9 +253,9 @@ jobs: - name: Publish Extension (Code Marketplace, nightly) if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') working-directory: ./editors/code - run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release + run: npx vsce publish --pat ${{ secrets.MARKETPLACE_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix - name: Publish Extension (OpenVSX, nightly) if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') working-directory: ./editors/code - run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix --pre-release + run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix diff --git a/.vscode/launch.json b/.vscode/launch.json index 021b8f048cf2..1e21214ffc4b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -78,7 +78,7 @@ "request": "launch", "runtimeExecutable": "${execPath}", "args": [ - "--disable-extension", "matklad.rust-analyzer", + "--disable-extension", "rust-lang.rust-analyzer", "--extensionDevelopmentPath=${workspaceFolder}/editors/code" ], "outFiles": [ diff --git a/Cargo.lock b/Cargo.lock index 703f0e5b8af9..7c6796d70bdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -710,6 +710,7 @@ dependencies = [ "ide-db", "itertools", "profile", + "serde_json", "sourcegen", "stdx", "syntax", diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 9b5a10acfbea..9580ce8007c7 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -6,7 +6,7 @@ //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how //! actual IO is done and lowered to input. -use std::{fmt, iter::FromIterator, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; +use std::{fmt, ops, panic::RefUnwindSafe, str::FromStr, sync::Arc}; use cfg::CfgOptions; use rustc_hash::{FxHashMap, FxHashSet}; 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 92dffa7f372e..4f626105a53d 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 @@ -295,13 +295,13 @@ fn test_concat_expand() { #[rustc_builtin_macro] macro_rules! concat {} -fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false); } +fn main() { concat!("foo", "r", 0, r#"bar"#, "\n", false, '"', '\0'); } "##, expect![[r##" #[rustc_builtin_macro] macro_rules! concat {} -fn main() { "foor0bar\nfalse"; } +fn main() { "foor0bar\nfalse\"\u{0}"; } "##]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 30d39d52f385..457e43925c63 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -885,7 +885,7 @@ macro_rules! m { ($t:ty) => ( fn bar() -> $ t {} ) } -fn bar() -> & 'a Baz {} +fn bar() -> &'a Baz {} fn bar() -> extern "Rust"fn() -> Ret {} "#]], @@ -1578,7 +1578,7 @@ macro_rules !register_methods { ($$($val: expr), *) = > { struct Foo; impl Foo { - $(fn $method()-> & 'static[u32] { + $(fn $method()-> &'static[u32] { &[$$($$val), *] } )* @@ -1591,10 +1591,10 @@ macro_rules !implement_methods { ($($val: expr), *) = > { struct Foo; impl Foo { - fn alpha()-> & 'static[u32] { + fn alpha()-> &'static[u32] { &[$($val), *] } - fn beta()-> & 'static[u32] { + fn beta()-> &'static[u32] { &[$($val), *] } } @@ -1602,10 +1602,10 @@ macro_rules !implement_methods { } struct Foo; impl Foo { - fn alpha() -> & 'static[u32] { + fn alpha() -> &'static[u32] { &[1, 2, 3] } - fn beta() -> & 'static[u32] { + fn beta() -> &'static[u32] { &[1, 2, 3] } } 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 2dff4adf2ee8..d2505e7cafe5 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/regression.rs @@ -166,7 +166,7 @@ macro_rules! int_base { } } #[stable(feature = "rust1", since = "1.0.0")] impl fmt::Binary for isize { - fn fmt(&self , f: &mut fmt::Formatter< '_>) -> fmt::Result { + fn fmt(&self , f: &mut fmt::Formatter<'_>) -> fmt::Result { Binary.fmt_int(*self as usize, f) } } @@ -724,7 +724,7 @@ macro_rules! delegate_impl { } } } -impl <> Data for & 'amut G where G: Data {} +impl <> Data for &'amut G where G: Data {} "##]], ); } diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs index 0710b1ac3d69..b8d2ca687c9e 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -78,7 +78,7 @@ m!(static bar: &'static str = "hello";); macro_rules! m { ($($t:tt)*) => { $($t)*} } -static bar: & 'static str = "hello"; +static bar: &'static str = "hello"; "#]], ); } 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 72c44a0fbcb2..029821e5e87f 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -87,7 +87,7 @@ fn foo() { bar.; blub } fn foo() { bar.; blub } fn foo() { - bar. ; + bar.; blub }"##]], ); diff --git a/crates/hir-def/src/nameres/mod_resolution.rs b/crates/hir-def/src/nameres/mod_resolution.rs index 52a620fe22f6..99f7f1b549e2 100644 --- a/crates/hir-def/src/nameres/mod_resolution.rs +++ b/crates/hir-def/src/nameres/mod_resolution.rs @@ -73,10 +73,12 @@ impl ModDir { candidate_files.push(self.dir_path.join_attr(attr_path, self.root_non_dir_owner)) } None if file_id.is_include_macro(db.upcast()) => { + let name = name.unescaped(); candidate_files.push(format!("{}.rs", name)); candidate_files.push(format!("{}/mod.rs", name)); } None => { + let name = name.unescaped(); candidate_files.push(format!("{}{}.rs", self.dir_path.0, name)); candidate_files.push(format!("{}{}/mod.rs", self.dir_path.0, name)); } diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs index 79a74873b4a4..3fa585574dee 100644 --- a/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -132,9 +132,9 @@ pub struct Bar; expect![[r#" crate Bar: t v - async: t + r#async: t - crate::async + crate::r#async Bar: t v "#]], ); diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs index 76da7c9f1ee8..8befa7f7da72 100644 --- a/crates/hir-expand/src/builtin_fn_macro.rs +++ b/crates/hir-expand/src/builtin_fn_macro.rs @@ -251,9 +251,13 @@ fn format_args_expand( } for arg in &mut args { // Remove `key =`. - if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint) + if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=') { - arg.token_trees.drain(..2); + // but not with `==` + if !matches!(arg.token_trees.get(2), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' ) + { + arg.token_trees.drain(..2); + } } } let _format_string = args.remove(0); @@ -357,6 +361,12 @@ fn unquote_str(lit: &tt::Literal) -> Option { token.value().map(|it| it.into_owned()) } +fn unquote_char(lit: &tt::Literal) -> Option { + let lit = ast::make::tokens::literal(&lit.to_string()); + let token = ast::Char::cast(lit)?; + token.value() +} + fn unquote_byte_string(lit: &tt::Literal) -> Option> { let lit = ast::make::tokens::literal(&lit.to_string()); let token = ast::ByteString::cast(lit)?; @@ -408,8 +418,12 @@ fn concat_expand( // concat works with string and char literals, so remove any quotes. // It also works with integer, float and boolean literals, so just use the rest // as-is. - let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); - text.push_str(&component); + if let Some(c) = unquote_char(it) { + text.push(c); + } else { + let component = unquote_str(it).unwrap_or_else(|| it.text.to_string()); + text.push_str(&component); + } } // handle boolean literals tt::TokenTree::Leaf(tt::Leaf::Ident(id)) diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index e46f43a878fe..893e6fe4b824 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -67,7 +67,6 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { preorder.skip_subtree(); continue; } - // In some other situations, we can fix things by just appending some tokens. let end_range = TextRange::empty(node.text_range().end()); match_ast! { @@ -194,7 +193,75 @@ pub(crate) fn fixup_syntax(node: &SyntaxNode) -> SyntaxFixups { } }, // FIXME: foo:: - // FIXME: for, match etc. + ast::MatchExpr(it) => { + if it.expr().is_none() { + let match_token = match it.match_token() { + Some(t) => t, + None => continue + }; + append.insert(match_token.into(), vec![ + SyntheticToken { + kind: SyntaxKind::IDENT, + text: "__ra_fixup".into(), + range: end_range, + id: EMPTY_ID + }, + ]); + } + if it.match_arm_list().is_none() { + // No match arms + append.insert(node.clone().into(), vec![ + SyntheticToken { + kind: SyntaxKind::L_CURLY, + text: "{".into(), + range: end_range, + id: EMPTY_ID, + }, + SyntheticToken { + kind: SyntaxKind::R_CURLY, + text: "}".into(), + range: end_range, + id: EMPTY_ID, + }, + ]); + } + }, + ast::ForExpr(it) => { + let for_token = match it.for_token() { + Some(token) => token, + None => continue + }; + + let [pat, in_token, iter] = [ + (SyntaxKind::UNDERSCORE, "_"), + (SyntaxKind::IN_KW, "in"), + (SyntaxKind::IDENT, "__ra_fixup") + ].map(|(kind, text)| SyntheticToken { kind, text: text.into(), range: end_range, id: EMPTY_ID}); + + if it.pat().is_none() && it.in_token().is_none() && it.iterable().is_none() { + append.insert(for_token.into(), vec![pat, in_token, iter]); + // does something funky -- see test case for_no_pat + } else if it.pat().is_none() { + append.insert(for_token.into(), vec![pat]); + } + + if it.loop_body().is_none() { + append.insert(node.clone().into(), vec![ + SyntheticToken { + kind: SyntaxKind::L_CURLY, + text: "{".into(), + range: end_range, + id: EMPTY_ID, + }, + SyntheticToken { + kind: SyntaxKind::R_CURLY, + text: "}".into(), + range: end_range, + id: EMPTY_ID, + }, + ]); + } + }, _ => (), } } @@ -287,6 +354,111 @@ mod tests { assert_eq!(tt.to_string(), original_as_tt.to_string()); } + #[test] + fn just_for_token() { + check( + r#" +fn foo() { + for +} +"#, + expect![[r#" +fn foo () {for _ in __ra_fixup {}} +"#]], + ) + } + + #[test] + fn for_no_iter_pattern() { + check( + r#" +fn foo() { + for {} +} +"#, + expect![[r#" +fn foo () {for _ in __ra_fixup {}} +"#]], + ) + } + + #[test] + fn for_no_body() { + check( + r#" +fn foo() { + for bar in qux +} +"#, + expect![[r#" +fn foo () {for bar in qux {}} +"#]], + ) + } + + // FIXME: https://github.com/rust-lang/rust-analyzer/pull/12937#discussion_r937633695 + #[test] + fn for_no_pat() { + check( + r#" +fn foo() { + for in qux { + + } +} +"#, + expect![[r#" +fn foo () {__ra_fixup} +"#]], + ) + } + + #[test] + fn match_no_expr_no_arms() { + check( + r#" +fn foo() { + match +} +"#, + expect![[r#" +fn foo () {match __ra_fixup {}} +"#]], + ) + } + + #[test] + fn match_expr_no_arms() { + check( + r#" +fn foo() { + match x { + + } +} +"#, + expect![[r#" +fn foo () {match x {}} +"#]], + ) + } + + #[test] + fn match_no_expr() { + check( + r#" +fn foo() { + match { + _ => {} + } +} +"#, + expect![[r#" +fn foo () {match __ra_fixup {}} +"#]], + ) + } + #[test] fn incomplete_field_expr_1() { check( @@ -296,7 +468,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a . __ra_fixup} +fn foo () {a .__ra_fixup} "#]], ) } @@ -306,11 +478,11 @@ fn foo () {a . __ra_fixup} check( r#" fn foo() { - a. ; + a.; } "#, expect![[r#" -fn foo () {a . __ra_fixup ;} +fn foo () {a .__ra_fixup ;} "#]], ) } @@ -320,12 +492,12 @@ fn foo () {a . __ra_fixup ;} check( r#" fn foo() { - a. ; + a.; bar(); } "#, expect![[r#" -fn foo () {a . __ra_fixup ; bar () ;} +fn foo () {a .__ra_fixup ; bar () ;} "#]], ) } @@ -353,7 +525,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {let x = a . __ra_fixup ;} +fn foo () {let x = a .__ra_fixup ;} "#]], ) } @@ -369,7 +541,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a . b ; bar () ;} +fn foo () {a .b ; bar () ;} "#]], ) } diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index fea09521e87c..d0f73ec8208c 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -22,7 +22,7 @@ pub struct ModPath { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct EscapedModPath<'a>(&'a ModPath); +pub struct UnescapedModPath<'a>(&'a ModPath); #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PathKind { @@ -102,8 +102,8 @@ impl ModPath { } } - pub fn escaped(&self) -> EscapedModPath<'_> { - EscapedModPath(self) + pub fn unescaped(&self) -> UnescapedModPath<'_> { + UnescapedModPath(self) } fn _fmt(&self, f: &mut fmt::Formatter<'_>, escaped: bool) -> fmt::Result { @@ -134,9 +134,9 @@ impl ModPath { } first_segment = false; if escaped { - segment.escaped().fmt(f)? - } else { segment.fmt(f)? + } else { + segment.unescaped().fmt(f)? }; } Ok(()) @@ -145,13 +145,13 @@ impl ModPath { impl Display for ModPath { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self._fmt(f, false) + self._fmt(f, true) } } -impl<'a> Display for EscapedModPath<'a> { +impl<'a> Display for UnescapedModPath<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0._fmt(f, true) + self.0._fmt(f, false) } } diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 47d191822d84..87c663eec8e8 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -7,12 +7,16 @@ use syntax::{ast, SmolStr, SyntaxKind}; /// `Name` is a wrapper around string, which is used in hir for both references /// and declarations. In theory, names should also carry hygiene info, but we are /// not there yet! +/// +/// Note that `Name` holds and prints escaped name i.e. prefixed with "r#" when it +/// is a raw identifier. Use [`unescaped()`][Name::unescaped] when you need the +/// name without "r#". #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Name(Repr); -/// `EscapedName` will add a prefix "r#" to the wrapped `Name` when it is a raw identifier +/// Wrapper of `Name` to print the name without "r#" even when it is a raw identifier. #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct EscapedName<'a>(&'a Name); +pub struct UnescapedName<'a>(&'a Name); #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] enum Repr { @@ -34,37 +38,26 @@ fn is_raw_identifier(name: &str) -> bool { is_keyword && !matches!(name, "self" | "crate" | "super" | "Self") } -impl<'a> fmt::Display for EscapedName<'a> { +impl<'a> fmt::Display for UnescapedName<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0 .0 { Repr::Text(text) => { - if is_raw_identifier(text) { - write!(f, "r#{}", &text) - } else { - fmt::Display::fmt(&text, f) - } + let text = text.strip_prefix("r#").unwrap_or(text); + fmt::Display::fmt(&text, f) } Repr::TupleField(idx) => fmt::Display::fmt(&idx, f), } } } -impl<'a> EscapedName<'a> { - pub fn is_escaped(&self) -> bool { - match &self.0 .0 { - Repr::Text(it) => is_raw_identifier(&it), - Repr::TupleField(_) => false, - } - } - - /// Returns the textual representation of this name as a [`SmolStr`]. - /// Prefer using this over [`ToString::to_string`] if possible as this conversion is cheaper in - /// the general case. +impl<'a> UnescapedName<'a> { + /// Returns the textual representation of this name as a [`SmolStr`]. Prefer using this over + /// [`ToString::to_string`] if possible as this conversion is cheaper in the general case. pub fn to_smol_str(&self) -> SmolStr { match &self.0 .0 { Repr::Text(it) => { - if is_raw_identifier(&it) { - SmolStr::from_iter(["r#", &it]) + if let Some(stripped) = it.strip_prefix("r#") { + SmolStr::new(stripped) } else { it.clone() } @@ -97,9 +90,11 @@ impl Name { /// Resolve a name from the text of token. fn resolve(raw_text: &str) -> Name { + // When `raw_text` starts with "r#" but the name does not coincide with any + // keyword, we never need the prefix so we strip it. match raw_text.strip_prefix("r#") { - Some(text) => Name::new_text(SmolStr::new(text)), - None => Name::new_text(raw_text.into()), + Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), + _ => Name::new_text(raw_text.into()), } } @@ -142,8 +137,15 @@ impl Name { } } - pub fn escaped(&self) -> EscapedName<'_> { - EscapedName(self) + pub fn unescaped(&self) -> UnescapedName<'_> { + UnescapedName(self) + } + + pub fn is_escaped(&self) -> bool { + match &self.0 { + Repr::Text(it) => it.starts_with("r#"), + Repr::TupleField(_) => false, + } } } diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs index 82f410ecda91..e839e97bf02d 100644 --- a/crates/hir-expand/src/quote.rs +++ b/crates/hir-expand/src/quote.rs @@ -196,8 +196,8 @@ impl_to_to_tokentrees! { tt::Literal => self { self }; tt::Ident => self { self }; tt::Punct => self { self }; - &str => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}}; - String => self { tt::Literal{text: format!("\"{}\"", self.escape_debug()).into(), id: tt::TokenId::unspecified()}} + &str => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}}; + String => self { tt::Literal{text: format!("\"{}\"", self.escape_default()).into(), id: tt::TokenId::unspecified()}} } #[cfg(test)] diff --git a/crates/hir-ty/src/consteval.rs b/crates/hir-ty/src/consteval.rs index 0495a4e64cac..6ecb6e6fd173 100644 --- a/crates/hir-ty/src/consteval.rs +++ b/crates/hir-ty/src/consteval.rs @@ -2,7 +2,6 @@ use std::{ collections::HashMap, - convert::TryInto, fmt::{Display, Write}, }; diff --git a/crates/hir-ty/src/infer/pat.rs b/crates/hir-ty/src/infer/pat.rs index 5e7320a5dd30..53259d66dec6 100644 --- a/crates/hir-ty/src/infer/pat.rs +++ b/crates/hir-ty/src/infer/pat.rs @@ -14,8 +14,9 @@ use crate::{ consteval::intern_const_scalar, infer::{BindingMode, Expectation, InferenceContext, TypeMismatch}, lower::lower_to_chalk_mutability, - static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt, - TyKind, + primitive::UintTy, + static_lifetime, ConcreteConst, ConstValue, Interner, Scalar, Substitution, Ty, TyBuilder, + TyExt, TyKind, }; use super::PatLike; @@ -294,7 +295,29 @@ impl<'a> InferenceContext<'a> { let start_ty = self.infer_expr(*start, &Expectation::has_type(expected.clone())); self.infer_expr(*end, &Expectation::has_type(start_ty)) } - Pat::Lit(expr) => self.infer_expr(*expr, &Expectation::has_type(expected.clone())), + &Pat::Lit(expr) => { + // FIXME: using `Option` here is a workaround until we can use if-let chains in stable. + let mut pat_ty = None; + + // Like slice patterns, byte string patterns can denote both `&[u8; N]` and `&[u8]`. + if let Expr::Literal(Literal::ByteString(_)) = self.body[expr] { + if let Some((inner, ..)) = expected.as_reference() { + let inner = self.resolve_ty_shallow(inner); + if matches!(inner.kind(Interner), TyKind::Slice(_)) { + let elem_ty = TyKind::Scalar(Scalar::Uint(UintTy::U8)).intern(Interner); + let slice_ty = TyKind::Slice(elem_ty).intern(Interner); + let ty = TyKind::Ref(Mutability::Not, static_lifetime(), slice_ty) + .intern(Interner); + self.write_expr_ty(expr, ty.clone()); + pat_ty = Some(ty); + } + } + } + + pat_ty.unwrap_or_else(|| { + self.infer_expr(expr, &Expectation::has_type(expected.clone())) + }) + } Pat::Box { inner } => match self.resolve_boxed_box() { Some(box_adt) => { let (inner_ty, alloc_ty) = match expected.as_adt() { @@ -343,7 +366,9 @@ fn is_non_ref_pat(body: &hir_def::body::Body, pat: PatId) -> bool { // FIXME: ConstBlock/Path/Lit might actually evaluate to ref, but inference is unimplemented. Pat::Path(..) => true, Pat::ConstBlock(..) => true, - Pat::Lit(expr) => !matches!(body[*expr], Expr::Literal(Literal::String(..))), + Pat::Lit(expr) => { + !matches!(body[*expr], Expr::Literal(Literal::String(..) | Literal::ByteString(..))) + } Pat::Bind { mode: BindingAnnotation::Mutable | BindingAnnotation::Unannotated, subpat: Some(subpat), diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 399553356b04..94efe7bc11a8 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -315,6 +315,51 @@ fn infer_pattern_match_string_literal() { ); } +#[test] +fn infer_pattern_match_byte_string_literal() { + check_infer_with_mismatches( + r#" + //- minicore: index + struct S; + impl core::ops::Index for [T; N] { + type Output = [u8]; + fn index(&self, index: core::ops::RangeFull) -> &Self::Output { + loop {} + } + } + fn test(v: [u8; 3]) { + if let b"foo" = &v[S] {} + if let b"foo" = &v {} + } + "#, + expect![[r#" + 105..109 'self': &[T; N] + 111..116 'index': {unknown} + 157..180 '{ ... }': &[u8] + 167..174 'loop {}': ! + 172..174 '{}': () + 191..192 'v': [u8; 3] + 203..261 '{ ...v {} }': () + 209..233 'if let...[S] {}': () + 212..230 'let b"... &v[S]': bool + 216..222 'b"foo"': &[u8] + 216..222 'b"foo"': &[u8] + 225..230 '&v[S]': &[u8] + 226..227 'v': [u8; 3] + 226..230 'v[S]': [u8] + 228..229 'S': S + 231..233 '{}': () + 238..259 'if let... &v {}': () + 241..256 'let b"foo" = &v': bool + 245..251 'b"foo"': &[u8; 3] + 245..251 'b"foo"': &[u8; 3] + 254..256 '&v': &[u8; 3] + 255..256 'v': [u8; 3] + 257..259 '{}': () + "#]], + ); +} + #[test] fn infer_pattern_match_or() { check_infer_with_mismatches( diff --git a/crates/hir/src/diagnostics.rs b/crates/hir/src/diagnostics.rs index 6c6c11ea4ebd..50374f4b3fe4 100644 --- a/crates/hir/src/diagnostics.rs +++ b/crates/hir/src/diagnostics.rs @@ -14,6 +14,7 @@ use crate::{MacroKind, Type}; macro_rules! diagnostics { ($($diag:ident,)*) => { + #[derive(Debug)] pub enum AnyDiagnostic {$( $diag(Box<$diag>), )*} diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f5e2e4430709..ae2896e19329 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -368,6 +368,7 @@ impl SourceAnalyzer { let local = if field.name_ref().is_some() { None } else { + // Shorthand syntax, resolve to the local let path = ModPath::from_segments(PathKind::Plain, once(local_name.clone())); match self.resolver.resolve_path_in_value_ns_fully(db.upcast(), &path) { Some(ValueNs::LocalBinding(pat_id)) => { diff --git a/crates/ide-assists/src/assist_context.rs b/crates/ide-assists/src/assist_context.rs index f9b426614257..8c7670e0cb71 100644 --- a/crates/ide-assists/src/assist_context.rs +++ b/crates/ide-assists/src/assist_context.rs @@ -1,28 +1,20 @@ //! See [`AssistContext`]. -use std::mem; - use hir::Semantics; -use ide_db::{ - base_db::{AnchoredPathBuf, FileId, FileRange}, - SnippetCap, -}; -use ide_db::{ - label::Label, - source_change::{FileSystemEdit, SourceChange}, - RootDatabase, -}; +use ide_db::base_db::{FileId, FileRange}; +use ide_db::{label::Label, RootDatabase}; use syntax::{ algo::{self, find_node_at_offset, find_node_at_range}, - AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxNode, SyntaxNodePtr, - SyntaxToken, TextRange, TextSize, TokenAtOffset, + AstNode, AstToken, Direction, SourceFile, SyntaxElement, SyntaxKind, SyntaxToken, TextRange, + TextSize, TokenAtOffset, }; -use text_edit::{TextEdit, TextEditBuilder}; use crate::{ assist_config::AssistConfig, Assist, AssistId, AssistKind, AssistResolveStrategy, GroupLabel, }; +pub(crate) use ide_db::source_change::{SourceChangeBuilder, TreeMutator}; + /// `AssistContext` allows to apply an assist or check if it could be applied. /// /// Assists use a somewhat over-engineered approach, given the current needs. @@ -163,7 +155,7 @@ impl Assists { id: AssistId, label: impl Into, target: TextRange, - f: impl FnOnce(&mut AssistBuilder), + f: impl FnOnce(&mut SourceChangeBuilder), ) -> Option<()> { let mut f = Some(f); self.add_impl(None, id, label.into(), target, &mut |it| f.take().unwrap()(it)) @@ -175,7 +167,7 @@ impl Assists { id: AssistId, label: impl Into, target: TextRange, - f: impl FnOnce(&mut AssistBuilder), + f: impl FnOnce(&mut SourceChangeBuilder), ) -> Option<()> { let mut f = Some(f); self.add_impl(Some(group), id, label.into(), target, &mut |it| f.take().unwrap()(it)) @@ -187,7 +179,7 @@ impl Assists { id: AssistId, label: String, target: TextRange, - f: &mut dyn FnMut(&mut AssistBuilder), + f: &mut dyn FnMut(&mut SourceChangeBuilder), ) -> Option<()> { if !self.is_allowed(&id) { return None; @@ -195,7 +187,7 @@ impl Assists { let mut trigger_signature_help = false; let source_change = if self.resolve.should_resolve(&id) { - let mut builder = AssistBuilder::new(self.file); + let mut builder = SourceChangeBuilder::new(self.file); f(&mut builder); trigger_signature_help = builder.trigger_signature_help; Some(builder.finish()) @@ -216,132 +208,3 @@ impl Assists { } } } - -pub(crate) struct AssistBuilder { - edit: TextEditBuilder, - file_id: FileId, - source_change: SourceChange, - trigger_signature_help: bool, - - /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. - mutated_tree: Option, -} - -pub(crate) struct TreeMutator { - immutable: SyntaxNode, - mutable_clone: SyntaxNode, -} - -impl TreeMutator { - pub(crate) fn new(immutable: &SyntaxNode) -> TreeMutator { - let immutable = immutable.ancestors().last().unwrap(); - let mutable_clone = immutable.clone_for_update(); - TreeMutator { immutable, mutable_clone } - } - - pub(crate) fn make_mut(&self, node: &N) -> N { - N::cast(self.make_syntax_mut(node.syntax())).unwrap() - } - - pub(crate) fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { - let ptr = SyntaxNodePtr::new(node); - ptr.to_node(&self.mutable_clone) - } -} - -impl AssistBuilder { - pub(crate) fn new(file_id: FileId) -> AssistBuilder { - AssistBuilder { - edit: TextEdit::builder(), - file_id, - source_change: SourceChange::default(), - trigger_signature_help: false, - mutated_tree: None, - } - } - - pub(crate) fn edit_file(&mut self, file_id: FileId) { - self.commit(); - self.file_id = file_id; - } - - fn commit(&mut self) { - if let Some(tm) = self.mutated_tree.take() { - algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) - } - - let edit = mem::take(&mut self.edit).finish(); - if !edit.is_empty() { - self.source_change.insert_source_edit(self.file_id, edit); - } - } - - pub(crate) fn make_mut(&mut self, node: N) -> N { - self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) - } - /// Returns a copy of the `node`, suitable for mutation. - /// - /// Syntax trees in rust-analyzer are typically immutable, and mutating - /// operations panic at runtime. However, it is possible to make a copy of - /// the tree and mutate the copy freely. Mutation is based on interior - /// mutability, and different nodes in the same tree see the same mutations. - /// - /// The typical pattern for an assist is to find specific nodes in the read - /// phase, and then get their mutable couterparts using `make_mut` in the - /// mutable state. - pub(crate) fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { - self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) - } - - /// Remove specified `range` of text. - pub(crate) fn delete(&mut self, range: TextRange) { - self.edit.delete(range) - } - /// Append specified `text` at the given `offset` - pub(crate) fn insert(&mut self, offset: TextSize, text: impl Into) { - self.edit.insert(offset, text.into()) - } - /// Append specified `snippet` at the given `offset` - pub(crate) fn insert_snippet( - &mut self, - _cap: SnippetCap, - offset: TextSize, - snippet: impl Into, - ) { - self.source_change.is_snippet = true; - self.insert(offset, snippet); - } - /// Replaces specified `range` of text with a given string. - pub(crate) fn replace(&mut self, range: TextRange, replace_with: impl Into) { - self.edit.replace(range, replace_with.into()) - } - /// Replaces specified `range` of text with a given `snippet`. - pub(crate) fn replace_snippet( - &mut self, - _cap: SnippetCap, - range: TextRange, - snippet: impl Into, - ) { - self.source_change.is_snippet = true; - self.replace(range, snippet); - } - pub(crate) fn replace_ast(&mut self, old: N, new: N) { - algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) - } - pub(crate) fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into) { - let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() }; - self.source_change.push_file_system_edit(file_system_edit); - } - pub(crate) fn move_file(&mut self, src: FileId, dst: AnchoredPathBuf) { - let file_system_edit = FileSystemEdit::MoveFile { src, dst }; - self.source_change.push_file_system_edit(file_system_edit); - } - pub(crate) fn trigger_signature_help(&mut self) { - self.trigger_signature_help = true; - } - - fn finish(mut self) -> SourceChange { - self.commit(); - mem::take(&mut self.source_change) - } -} diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index c808c010c672..62cf5ab4f37a 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -944,7 +944,7 @@ foo!(); struct Foo(usize); impl FooB for Foo { - $0fn foo< 'lt>(& 'lt self){} + $0fn foo<'lt>(&'lt self){} } "#, ) diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index 4ab8e93a2909..d8f522708460 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -5,7 +5,7 @@ use syntax::{ match_ast, SyntaxNode, }; -use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; // Assist: convert_tuple_struct_to_named_struct // @@ -80,7 +80,7 @@ pub(crate) fn convert_tuple_struct_to_named_struct( fn edit_struct_def( ctx: &AssistContext<'_>, - edit: &mut AssistBuilder, + edit: &mut SourceChangeBuilder, strukt: &Either, tuple_fields: ast::TupleFieldList, names: Vec, @@ -122,7 +122,7 @@ fn edit_struct_def( fn edit_struct_references( ctx: &AssistContext<'_>, - edit: &mut AssistBuilder, + edit: &mut SourceChangeBuilder, strukt: Either, names: &[ast::Name], ) { @@ -132,7 +132,7 @@ fn edit_struct_references( }; let usages = strukt_def.usages(&ctx.sema).include_self_refs().all(); - let edit_node = |edit: &mut AssistBuilder, node: SyntaxNode| -> Option<()> { + let edit_node = |edit: &mut SourceChangeBuilder, node: SyntaxNode| -> Option<()> { match_ast! { match node { ast::TupleStructPat(tuple_struct_pat) => { @@ -203,7 +203,7 @@ fn edit_struct_references( fn edit_field_references( ctx: &AssistContext<'_>, - edit: &mut AssistBuilder, + edit: &mut SourceChangeBuilder, fields: impl Iterator, names: &[ast::Name], ) { diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index c1f57532bb29..dc581ff3bd2c 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -8,7 +8,7 @@ use syntax::{ TextRange, }; -use crate::assist_context::{AssistBuilder, AssistContext, Assists}; +use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder}; // Assist: destructure_tuple_binding // @@ -151,7 +151,7 @@ struct TupleData { } fn edit_tuple_assignment( ctx: &AssistContext<'_>, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, data: &TupleData, in_sub_pattern: bool, ) { @@ -195,7 +195,7 @@ fn edit_tuple_assignment( fn edit_tuple_usages( data: &TupleData, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, ctx: &AssistContext<'_>, in_sub_pattern: bool, ) { @@ -211,7 +211,7 @@ fn edit_tuple_usages( } fn edit_tuple_usage( ctx: &AssistContext<'_>, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, usage: &FileReference, data: &TupleData, in_sub_pattern: bool, @@ -239,7 +239,7 @@ fn edit_tuple_usage( fn edit_tuple_field_usage( ctx: &AssistContext<'_>, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, data: &TupleData, index: TupleIndex, ) { diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index a93648f2d315..dfb565212646 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -20,7 +20,7 @@ use syntax::{ SyntaxNode, T, }; -use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; // Assist: extract_struct_from_enum_variant // @@ -374,7 +374,7 @@ fn apply_references( fn process_references( ctx: &AssistContext<'_>, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, visited_modules: &mut FxHashSet, enum_module_def: &ModuleDef, variant_hir_name: &Name, diff --git a/crates/ide-assists/src/handlers/generate_deref.rs b/crates/ide-assists/src/handlers/generate_deref.rs index b9637ee8d7c6..b484635121eb 100644 --- a/crates/ide-assists/src/handlers/generate_deref.rs +++ b/crates/ide-assists/src/handlers/generate_deref.rs @@ -8,7 +8,7 @@ use syntax::{ }; use crate::{ - assist_context::{AssistBuilder, AssistContext, Assists}, + assist_context::{AssistContext, Assists, SourceChangeBuilder}, utils::generate_trait_impl_text, AssistId, AssistKind, }; @@ -120,7 +120,7 @@ fn generate_tuple_deref(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<() } fn generate_edit( - edit: &mut AssistBuilder, + edit: &mut SourceChangeBuilder, strukt: ast::Struct, field_type_syntax: &SyntaxNode, field_name: impl Display, diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index ce91dd23703b..2fc754e3e50d 100644 --- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -5,7 +5,7 @@ use syntax::{ AstNode, TextRange, }; -use crate::{assist_context::AssistBuilder, AssistContext, AssistId, AssistKind, Assists}; +use crate::{assist_context::SourceChangeBuilder, AssistContext, AssistId, AssistKind, Assists}; static ASSIST_NAME: &str = "introduce_named_lifetime"; static ASSIST_LABEL: &str = "Introduce named lifetime"; @@ -140,7 +140,7 @@ enum NeedsLifetime { } impl NeedsLifetime { - fn make_mut(self, builder: &mut AssistBuilder) -> Self { + fn make_mut(self, builder: &mut SourceChangeBuilder) -> Self { match self { Self::SelfParam(it) => Self::SelfParam(builder.make_mut(it)), Self::RefType(it) => Self::RefType(builder.make_mut(it)), diff --git a/crates/ide-assists/src/handlers/remove_unused_param.rs b/crates/ide-assists/src/handlers/remove_unused_param.rs index 59ea94ea1ff6..bd2e8fbe3896 100644 --- a/crates/ide-assists/src/handlers/remove_unused_param.rs +++ b/crates/ide-assists/src/handlers/remove_unused_param.rs @@ -8,7 +8,8 @@ use syntax::{ use SyntaxKind::WHITESPACE; use crate::{ - assist_context::AssistBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, Assists, + assist_context::SourceChangeBuilder, utils::next_prev, AssistContext, AssistId, AssistKind, + Assists, }; // Assist: remove_unused_param @@ -88,7 +89,7 @@ pub(crate) fn remove_unused_param(acc: &mut Assists, ctx: &AssistContext<'_>) -> fn process_usages( ctx: &AssistContext<'_>, - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, file_id: FileId, references: Vec, arg_to_remove: usize, diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index bd50208da5ff..d139f78a6f36 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -10,7 +10,7 @@ use syntax::{ }; use crate::{ - assist_context::{AssistBuilder, AssistContext, Assists}, + assist_context::{AssistContext, Assists, SourceChangeBuilder}, utils::{ add_trait_assoc_items_to_impl, filter_assoc_items, gen_trait_fn_body, generate_trait_impl_text, render_snippet, Cursor, DefaultMethods, @@ -224,7 +224,7 @@ fn impl_def_from_trait( } fn update_attribute( - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, old_derives: &[ast::Path], old_tree: &ast::TokenTree, old_trait_path: &ast::Path, diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 3e61d0741d3f..103e3259fa2e 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -20,7 +20,7 @@ use syntax::{ SyntaxNode, TextRange, TextSize, T, }; -use crate::assist_context::{AssistBuilder, AssistContext}; +use crate::assist_context::{AssistContext, SourceChangeBuilder}; pub(crate) mod suggest_name; mod gen_trait_fn_body; @@ -484,7 +484,7 @@ fn generate_impl_text_inner(adt: &ast::Adt, trait_text: Option<&str>, code: &str } pub(crate) fn add_method_to_adt( - builder: &mut AssistBuilder, + builder: &mut SourceChangeBuilder, adt: &ast::Adt, impl_def: Option, method: &str, diff --git a/crates/ide-completion/src/completions.rs b/crates/ide-completion/src/completions.rs index 72579e6026ae..55c3e28392a4 100644 --- a/crates/ide-completion/src/completions.rs +++ b/crates/ide-completion/src/completions.rs @@ -617,7 +617,6 @@ pub(super) fn complete_name_ref( dot::complete_undotted_self(acc, ctx, path_ctx, expr_ctx); item_list::complete_item_list_in_expr(acc, ctx, path_ctx, expr_ctx); - record::complete_record_expr_func_update(acc, ctx, path_ctx, expr_ctx); snippet::complete_expr_snippet(acc, ctx, path_ctx, expr_ctx); } PathKind::Type { location } => { diff --git a/crates/ide-completion/src/completions/expr.rs b/crates/ide-completion/src/completions/expr.rs index 5d0ddaaf2a22..4d66af9e8d5b 100644 --- a/crates/ide-completion/src/completions/expr.rs +++ b/crates/ide-completion/src/completions/expr.rs @@ -1,8 +1,10 @@ //! Completion of names from the current scope in expression position. use hir::ScopeDef; +use syntax::ast; use crate::{ + completions::record::add_default_update, context::{ExprCtx, PathCompletionCtx, Qualified}, CompletionContext, Completions, }; @@ -219,60 +221,78 @@ pub(crate) fn complete_expr_path( _ => (), }); - if is_func_update.is_none() { - let mut add_keyword = - |kw, snippet| acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet); + match is_func_update { + Some(record_expr) => { + let ty = ctx.sema.type_of_expr(&ast::Expr::RecordExpr(record_expr.clone())); - if !in_block_expr { - add_keyword("unsafe", "unsafe {\n $0\n}"); - } - add_keyword("match", "match $1 {\n $0\n}"); - add_keyword("while", "while $1 {\n $0\n}"); - add_keyword("while let", "while let $1 = $2 {\n $0\n}"); - add_keyword("loop", "loop {\n $0\n}"); - if in_match_guard { - add_keyword("if", "if $0"); - } else { - add_keyword("if", "if $1 {\n $0\n}"); + match ty.as_ref().and_then(|t| t.original.as_adt()) { + Some(hir::Adt::Union(_)) => (), + _ => { + cov_mark::hit!(functional_update); + let missing_fields = + ctx.sema.record_literal_missing_fields(record_expr); + if !missing_fields.is_empty() { + add_default_update(acc, ctx, ty); + } + } + }; } - add_keyword("if let", "if let $1 = $2 {\n $0\n}"); - add_keyword("for", "for $1 in $2 {\n $0\n}"); - add_keyword("true", "true"); - add_keyword("false", "false"); + None => { + let mut add_keyword = |kw, snippet| { + acc.add_keyword_snippet_expr(ctx, incomplete_let, kw, snippet) + }; - if in_condition || in_block_expr { - add_keyword("let", "let"); - } + if !in_block_expr { + add_keyword("unsafe", "unsafe {\n $0\n}"); + } + add_keyword("match", "match $1 {\n $0\n}"); + add_keyword("while", "while $1 {\n $0\n}"); + add_keyword("while let", "while let $1 = $2 {\n $0\n}"); + add_keyword("loop", "loop {\n $0\n}"); + if in_match_guard { + add_keyword("if", "if $0"); + } else { + add_keyword("if", "if $1 {\n $0\n}"); + } + add_keyword("if let", "if let $1 = $2 {\n $0\n}"); + add_keyword("for", "for $1 in $2 {\n $0\n}"); + add_keyword("true", "true"); + add_keyword("false", "false"); - if after_if_expr { - add_keyword("else", "else {\n $0\n}"); - add_keyword("else if", "else if $1 {\n $0\n}"); - } + if in_condition || in_block_expr { + add_keyword("let", "let"); + } - if wants_mut_token { - add_keyword("mut", "mut "); - } + if after_if_expr { + add_keyword("else", "else {\n $0\n}"); + add_keyword("else if", "else if $1 {\n $0\n}"); + } - if in_loop_body { - if in_block_expr { - add_keyword("continue", "continue;"); - add_keyword("break", "break;"); - } else { - add_keyword("continue", "continue"); - add_keyword("break", "break"); + if wants_mut_token { + add_keyword("mut", "mut "); + } + + if in_loop_body { + if in_block_expr { + add_keyword("continue", "continue;"); + add_keyword("break", "break;"); + } else { + add_keyword("continue", "continue"); + add_keyword("break", "break"); + } } - } - if let Some(ty) = innermost_ret_ty { - add_keyword( - "return", - match (in_block_expr, ty.is_unit()) { - (true, true) => "return ;", - (true, false) => "return;", - (false, true) => "return $0", - (false, false) => "return", - }, - ); + if let Some(ty) = innermost_ret_ty { + add_keyword( + "return", + match (in_block_expr, ty.is_unit()) { + (true, true) => "return ;", + (true, false) => "return;", + (false, true) => "return $0", + (false, false) => "return", + }, + ); + } } } } 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 e9256803cc4f..785db6fde1d5 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -233,7 +233,8 @@ fn add_type_alias_impl( type_alias: hir::TypeAlias, ) { let alias_name = type_alias.name(ctx.db); - let (alias_name, escaped_name) = (alias_name.to_smol_str(), alias_name.escaped().to_smol_str()); + let (alias_name, escaped_name) = + (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str()); let label = format!("type {} =", alias_name); let replacement = format!("type {} = ", escaped_name); diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index 1c9042390d3b..bfb98b9f2777 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -3,7 +3,7 @@ use ide_db::SymbolKind; use syntax::ast::{self, Expr}; use crate::{ - context::{DotAccess, DotAccessKind, ExprCtx, PathCompletionCtx, PatternContext, Qualified}, + context::{DotAccess, DotAccessKind, PatternContext}, CompletionContext, CompletionItem, CompletionItemKind, CompletionRelevance, CompletionRelevancePostfixMatch, Completions, }; @@ -14,7 +14,24 @@ pub(crate) fn complete_record_pattern_fields( pattern_ctx: &PatternContext, ) { if let PatternContext { record_pat: Some(record_pat), .. } = pattern_ctx { - complete_fields(acc, ctx, ctx.sema.record_pattern_missing_fields(record_pat)); + let ty = ctx.sema.type_of_pat(&ast::Pat::RecordPat(record_pat.clone())); + let missing_fields = match ty.as_ref().and_then(|t| t.original.as_adt()) { + Some(hir::Adt::Union(un)) => { + // ctx.sema.record_pat_missing_fields will always return + // an empty Vec on a union literal. This is normally + // reasonable, but here we'd like to present the full list + // of fields if the literal is empty. + let were_fields_specified = + record_pat.record_pat_field_list().and_then(|fl| fl.fields().next()).is_some(); + + match were_fields_specified { + false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(), + true => return, + } + } + _ => ctx.sema.record_pattern_missing_fields(record_pat), + }; + complete_fields(acc, ctx, missing_fields); } } @@ -42,8 +59,13 @@ pub(crate) fn complete_record_expr_fields( } _ => { let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); - add_default_update(acc, ctx, ty, &missing_fields); + + if !missing_fields.is_empty() { + cov_mark::hit!(functional_update_field); + add_default_update(acc, ctx, ty); + } if dot_prefix { + cov_mark::hit!(functional_update_one_dot); let mut item = CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), ".."); item.insert_text("."); @@ -56,41 +78,18 @@ pub(crate) fn complete_record_expr_fields( complete_fields(acc, ctx, missing_fields); } -// FIXME: This should probably be part of complete_path_expr -pub(crate) fn complete_record_expr_func_update( - acc: &mut Completions, - ctx: &CompletionContext<'_>, - path_ctx: &PathCompletionCtx, - expr_ctx: &ExprCtx, -) { - if !matches!(path_ctx.qualified, Qualified::No) { - return; - } - if let ExprCtx { is_func_update: Some(record_expr), .. } = expr_ctx { - let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone())); - - match ty.as_ref().and_then(|t| t.original.as_adt()) { - Some(hir::Adt::Union(_)) => (), - _ => { - let missing_fields = ctx.sema.record_literal_missing_fields(record_expr); - add_default_update(acc, ctx, ty, &missing_fields); - } - }; - } -} - -fn add_default_update( +pub(crate) fn add_default_update( acc: &mut Completions, ctx: &CompletionContext<'_>, ty: Option, - missing_fields: &[(hir::Field, hir::Type)], ) { let default_trait = ctx.famous_defs().core_default_Default(); - let impl_default_trait = default_trait + let impls_default_trait = default_trait .zip(ty.as_ref()) .map_or(false, |(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[])); - if impl_default_trait && !missing_fields.is_empty() { + if impls_default_trait { // FIXME: This should make use of scope_def like completions so we get all the other goodies + // that is we should handle this like actually completing the default function let completion_text = "..Default::default()"; let mut item = CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text); let completion_text = diff --git a/crates/ide-completion/src/context.rs b/crates/ide-completion/src/context.rs index e35f79d2b695..759742d34723 100644 --- a/crates/ide-completion/src/context.rs +++ b/crates/ide-completion/src/context.rs @@ -134,6 +134,7 @@ pub(crate) struct ExprCtx { pub(crate) in_condition: bool, pub(crate) incomplete_let: bool, pub(crate) ref_expr_parent: Option, + /// The surrounding RecordExpression we are completing a functional update pub(crate) is_func_update: Option, pub(crate) self_param: Option, pub(crate) innermost_ret_ty: Option, diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 946134b0ff95..693007ca3071 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -117,7 +117,7 @@ pub(crate) fn render_field( ) -> CompletionItem { let is_deprecated = ctx.is_deprecated(field); let name = field.name(ctx.db()); - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let mut item = CompletionItem::new( SymbolKind::Field, ctx.source_range(), @@ -283,8 +283,8 @@ fn render_resolution_path( let name = local_name.to_smol_str(); let mut item = render_resolution_simple_(ctx, &local_name, import_to_add, resolution); - if local_name.escaped().is_escaped() { - item.insert_text(local_name.escaped().to_smol_str()); + if local_name.is_escaped() { + item.insert_text(local_name.to_smol_str()); } // Add `<>` for generic types let type_path_no_ty_args = matches!( @@ -306,7 +306,7 @@ fn render_resolution_path( item.lookup_by(name.clone()) .label(SmolStr::from_iter([&name, "<…>"])) .trigger_call_info() - .insert_snippet(cap, format!("{}<$0>", local_name.escaped())); + .insert_snippet(cap, format!("{}<$0>", local_name)); } } } @@ -342,7 +342,8 @@ fn render_resolution_simple_( let ctx = ctx.import_to_add(import_to_add); let kind = res_to_kind(resolution); - let mut item = CompletionItem::new(kind, ctx.source_range(), local_name.to_smol_str()); + let mut item = + CompletionItem::new(kind, ctx.source_range(), local_name.unescaped().to_smol_str()); item.set_relevance(ctx.completion_relevance()) .set_documentation(scope_def_docs(db, resolution)) .set_deprecated(scope_def_is_deprecated(&ctx, resolution)); diff --git a/crates/ide-completion/src/render/const_.rs b/crates/ide-completion/src/render/const_.rs index a810eef18dd1..93ea825e0042 100644 --- a/crates/ide-completion/src/render/const_.rs +++ b/crates/ide-completion/src/render/const_.rs @@ -13,7 +13,7 @@ pub(crate) fn render_const(ctx: RenderContext<'_>, const_: hir::Const) -> Option fn render(ctx: RenderContext<'_>, const_: hir::Const) -> Option { let db = ctx.db(); let name = const_.name(db)?; - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let detail = const_.display(db).to_string(); let mut item = CompletionItem::new(SymbolKind::Const, ctx.source_range(), name.clone()); diff --git a/crates/ide-completion/src/render/function.rs b/crates/ide-completion/src/render/function.rs index 4b5535718c5d..9cf64691298e 100644 --- a/crates/ide-completion/src/render/function.rs +++ b/crates/ide-completion/src/render/function.rs @@ -52,10 +52,10 @@ fn render( let (call, escaped_call) = match &func_kind { FuncKind::Method(_, Some(receiver)) => ( - format!("{}.{}", receiver, &name).into(), - format!("{}.{}", receiver.escaped(), name.escaped()).into(), + format!("{}.{}", receiver.unescaped(), name.unescaped()).into(), + format!("{}.{}", receiver, name).into(), ), - _ => (name.to_smol_str(), name.escaped().to_smol_str()), + _ => (name.unescaped().to_smol_str(), name.to_smol_str()), }; let mut item = CompletionItem::new( if func.self_param(db).is_some() { @@ -96,7 +96,7 @@ fn render( item.set_documentation(ctx.docs(func)) .set_deprecated(ctx.is_deprecated(func) || ctx.is_deprecated_assoc_item(func)) .detail(detail(db, func)) - .lookup_by(name.to_smol_str()); + .lookup_by(name.unescaped().to_smol_str()); match ctx.completion.config.snippet_cap { Some(cap) => { diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 91a253f8fc8c..707dea206be5 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -73,7 +73,7 @@ fn render( None => (name.clone().into(), name.into(), false), }; let (qualified_name, escaped_qualified_name) = - (qualified_name.to_string(), qualified_name.escaped().to_string()); + (qualified_name.unescaped().to_string(), qualified_name.to_string()); let snippet_cap = ctx.snippet_cap(); let mut rendered = match kind { diff --git a/crates/ide-completion/src/render/macro_.rs b/crates/ide-completion/src/render/macro_.rs index ca2269f1398f..eabd0bd17d65 100644 --- a/crates/ide-completion/src/render/macro_.rs +++ b/crates/ide-completion/src/render/macro_.rs @@ -46,7 +46,7 @@ fn render( ctx.source_range() }; - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let docs = ctx.docs(macro_); let docs_str = docs.as_ref().map(Documentation::as_str).unwrap_or_default(); let is_fn_like = macro_.is_fn_like(completion.db); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 34a384f2f7ae..1c1299e33b67 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -31,7 +31,7 @@ pub(crate) fn render_struct_pat( } let name = local_name.unwrap_or_else(|| strukt.name(ctx.db())); - let (name, escaped_name) = (name.to_smol_str(), name.escaped().to_smol_str()); + let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let kind = strukt.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; @@ -53,10 +53,10 @@ pub(crate) fn render_variant_pat( let (visible_fields, fields_omitted) = visible_fields(ctx.completion, &fields, variant)?; let (name, escaped_name) = match path { - Some(path) => (path.to_string().into(), path.escaped().to_string().into()), + Some(path) => (path.unescaped().to_string().into(), path.to_string().into()), None => { let name = local_name.unwrap_or_else(|| variant.name(ctx.db())); - (name.to_smol_str(), name.escaped().to_smol_str()) + (name.unescaped().to_smol_str(), name.to_smol_str()) } }; @@ -146,7 +146,7 @@ fn render_record_as_pat( format!( "{name} {{ {}{} }}", fields.enumerate().format_with(", ", |(idx, field), f| { - f(&format_args!("{}${}", field.name(db).escaped(), idx + 1)) + f(&format_args!("{}${}", field.name(db), idx + 1)) }), if fields_omitted { ", .." } else { "" }, name = name @@ -155,7 +155,7 @@ fn render_record_as_pat( None => { format!( "{name} {{ {}{} }}", - fields.map(|field| field.name(db).escaped().to_smol_str()).format(", "), + fields.map(|field| field.name(db).to_smol_str()).format(", "), if fields_omitted { ", .." } else { "" }, name = name ) diff --git a/crates/ide-completion/src/render/type_alias.rs b/crates/ide-completion/src/render/type_alias.rs index f1b23c76e7b4..de919429f2f9 100644 --- a/crates/ide-completion/src/render/type_alias.rs +++ b/crates/ide-completion/src/render/type_alias.rs @@ -32,11 +32,11 @@ fn render( let name = type_alias.name(db); let (name, escaped_name) = if with_eq { ( + SmolStr::from_iter([&name.unescaped().to_smol_str(), " = "]), SmolStr::from_iter([&name.to_smol_str(), " = "]), - SmolStr::from_iter([&name.escaped().to_smol_str(), " = "]), ) } else { - (name.to_smol_str(), name.escaped().to_smol_str()) + (name.unescaped().to_smol_str(), name.to_smol_str()) }; let detail = type_alias.display(db).to_string(); diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index 9c9540a9bd96..bb32330f276d 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -21,8 +21,8 @@ pub(crate) fn render_union_literal( let name = local_name.unwrap_or_else(|| un.name(ctx.db())); let (qualified_name, escaped_qualified_name) = match path { - Some(p) => (p.to_string(), p.escaped().to_string()), - None => (name.to_string(), name.escaped().to_string()), + Some(p) => (p.unescaped().to_string(), p.to_string()), + None => (name.unescaped().to_string(), name.to_string()), }; let mut item = CompletionItem::new( @@ -42,15 +42,15 @@ pub(crate) fn render_union_literal( format!( "{} {{ ${{1|{}|}}: ${{2:()}} }}$0", escaped_qualified_name, - fields.iter().map(|field| field.name(ctx.db()).escaped().to_smol_str()).format(",") + fields.iter().map(|field| field.name(ctx.db()).to_smol_str()).format(",") ) } else { format!( "{} {{ {} }}", escaped_qualified_name, - fields.iter().format_with(", ", |field, f| { - f(&format_args!("{}: ()", field.name(ctx.db()).escaped())) - }) + fields + .iter() + .format_with(", ", |field, f| { f(&format_args!("{}: ()", field.name(ctx.db()))) }) ) }; diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index 003a0c11ed2f..664845330eb8 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -24,9 +24,9 @@ pub(crate) fn render_record_lit( ) -> RenderedLiteral { let completions = fields.iter().enumerate().format_with(", ", |(idx, field), f| { if snippet_cap.is_some() { - f(&format_args!("{}: ${{{}:()}}", field.name(db).escaped(), idx + 1)) + f(&format_args!("{}: ${{{}:()}}", field.name(db), idx + 1)) } else { - f(&format_args!("{}: ()", field.name(db).escaped())) + f(&format_args!("{}: ()", field.name(db))) } }); diff --git a/crates/ide-completion/src/tests/record.rs b/crates/ide-completion/src/tests/record.rs index f6accc68e5e8..328faaa060f0 100644 --- a/crates/ide-completion/src/tests/record.rs +++ b/crates/ide-completion/src/tests/record.rs @@ -103,8 +103,9 @@ fn foo(f: Struct) { } #[test] -fn functional_update() { - // FIXME: This should filter out all completions that do not have the type `Foo` +fn in_functional_update() { + cov_mark::check!(functional_update); + check( r#" //- minicore:default @@ -116,13 +117,21 @@ impl Default for Foo { fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; - let foo2 = Foo { thing, $0 } + let foo2 = Foo { thing, ..$0 } } "#, expect![[r#" fd ..Default::default() - fd foo1 u32 - fd foo2 u32 + fn main() fn() + lc foo Foo + lc thing i32 + md core + st Foo + st Foo {…} Foo { foo1: u32, foo2: u32 } + tt Default + bt u32 + kw crate:: + kw self:: "#]], ); check( @@ -136,14 +145,19 @@ impl Default for Foo { fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; - let foo2 = Foo { thing, .$0 } + let foo2 = Foo { thing, ..Default::$0 } } "#, expect![[r#" - fd ..Default::default() - sn .. + fn default() (as Default) fn() -> Self "#]], ); +} + +#[test] +fn functional_update_no_dot() { + cov_mark::check!(functional_update_field); + // FIXME: This should filter out all completions that do not have the type `Foo` check( r#" //- minicore:default @@ -155,23 +169,20 @@ impl Default for Foo { fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; - let foo2 = Foo { thing, ..$0 } + let foo2 = Foo { thing, $0 } } "#, expect![[r#" fd ..Default::default() - fn main() fn() - lc foo Foo - lc thing i32 - md core - st Foo - st Foo {…} Foo { foo1: u32, foo2: u32 } - tt Default - bt u32 - kw crate:: - kw self:: + fd foo1 u32 + fd foo2 u32 "#]], ); +} + +#[test] +fn functional_update_one_dot() { + cov_mark::check!(functional_update_one_dot); check( r#" //- minicore:default @@ -183,11 +194,12 @@ impl Default for Foo { fn main() { let thing = 1; let foo = Foo { foo1: 0, foo2: 0 }; - let foo2 = Foo { thing, ..Default::$0 } + let foo2 = Foo { thing, .$0 } } "#, expect![[r#" - fn default() (as Default) fn() -> Self + fd ..Default::default() + sn .. "#]], ); } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index bd038cdaa068..9eaabeec7a4e 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -4,7 +4,7 @@ //! get a super-set of matches. Then, we we confirm each match using precise //! name resolution. -use std::{convert::TryInto, mem, sync::Arc}; +use std::{mem, sync::Arc}; use base_db::{FileId, FileRange, SourceDatabase, SourceDatabaseExt}; use hir::{DefWithBody, HasAttrs, HasSource, InFile, ModuleSource, Semantics, Visibility}; diff --git a/crates/ide-db/src/source_change.rs b/crates/ide-db/src/source_change.rs index 8132c73ef26b..21314ad74ef9 100644 --- a/crates/ide-db/src/source_change.rs +++ b/crates/ide-db/src/source_change.rs @@ -3,12 +3,15 @@ //! //! It can be viewed as a dual for `Change`. -use std::{collections::hash_map::Entry, iter}; +use std::{collections::hash_map::Entry, iter, mem}; use base_db::{AnchoredPathBuf, FileId}; use rustc_hash::FxHashMap; use stdx::never; -use text_edit::TextEdit; +use syntax::{algo, AstNode, SyntaxNode, SyntaxNodePtr, TextRange, TextSize}; +use text_edit::{TextEdit, TextEditBuilder}; + +use crate::SnippetCap; #[derive(Default, Debug, Clone)] pub struct SourceChange { @@ -81,6 +84,135 @@ impl From> for SourceChange { } } +pub struct SourceChangeBuilder { + pub edit: TextEditBuilder, + pub file_id: FileId, + pub source_change: SourceChange, + pub trigger_signature_help: bool, + + /// Maps the original, immutable `SyntaxNode` to a `clone_for_update` twin. + pub mutated_tree: Option, +} + +pub struct TreeMutator { + immutable: SyntaxNode, + mutable_clone: SyntaxNode, +} + +impl TreeMutator { + pub fn new(immutable: &SyntaxNode) -> TreeMutator { + let immutable = immutable.ancestors().last().unwrap(); + let mutable_clone = immutable.clone_for_update(); + TreeMutator { immutable, mutable_clone } + } + + pub fn make_mut(&self, node: &N) -> N { + N::cast(self.make_syntax_mut(node.syntax())).unwrap() + } + + pub fn make_syntax_mut(&self, node: &SyntaxNode) -> SyntaxNode { + let ptr = SyntaxNodePtr::new(node); + ptr.to_node(&self.mutable_clone) + } +} + +impl SourceChangeBuilder { + pub fn new(file_id: FileId) -> SourceChangeBuilder { + SourceChangeBuilder { + edit: TextEdit::builder(), + file_id, + source_change: SourceChange::default(), + trigger_signature_help: false, + mutated_tree: None, + } + } + + pub fn edit_file(&mut self, file_id: FileId) { + self.commit(); + self.file_id = file_id; + } + + fn commit(&mut self) { + if let Some(tm) = self.mutated_tree.take() { + algo::diff(&tm.immutable, &tm.mutable_clone).into_text_edit(&mut self.edit) + } + + let edit = mem::take(&mut self.edit).finish(); + if !edit.is_empty() { + self.source_change.insert_source_edit(self.file_id, edit); + } + } + + pub fn make_mut(&mut self, node: N) -> N { + self.mutated_tree.get_or_insert_with(|| TreeMutator::new(node.syntax())).make_mut(&node) + } + /// Returns a copy of the `node`, suitable for mutation. + /// + /// Syntax trees in rust-analyzer are typically immutable, and mutating + /// operations panic at runtime. However, it is possible to make a copy of + /// the tree and mutate the copy freely. Mutation is based on interior + /// mutability, and different nodes in the same tree see the same mutations. + /// + /// The typical pattern for an assist is to find specific nodes in the read + /// phase, and then get their mutable couterparts using `make_mut` in the + /// mutable state. + pub fn make_syntax_mut(&mut self, node: SyntaxNode) -> SyntaxNode { + self.mutated_tree.get_or_insert_with(|| TreeMutator::new(&node)).make_syntax_mut(&node) + } + + /// Remove specified `range` of text. + pub fn delete(&mut self, range: TextRange) { + self.edit.delete(range) + } + /// Append specified `text` at the given `offset` + pub fn insert(&mut self, offset: TextSize, text: impl Into) { + self.edit.insert(offset, text.into()) + } + /// Append specified `snippet` at the given `offset` + pub fn insert_snippet( + &mut self, + _cap: SnippetCap, + offset: TextSize, + snippet: impl Into, + ) { + self.source_change.is_snippet = true; + self.insert(offset, snippet); + } + /// Replaces specified `range` of text with a given string. + pub fn replace(&mut self, range: TextRange, replace_with: impl Into) { + self.edit.replace(range, replace_with.into()) + } + /// Replaces specified `range` of text with a given `snippet`. + pub fn replace_snippet( + &mut self, + _cap: SnippetCap, + range: TextRange, + snippet: impl Into, + ) { + self.source_change.is_snippet = true; + self.replace(range, snippet); + } + pub fn replace_ast(&mut self, old: N, new: N) { + algo::diff(old.syntax(), new.syntax()).into_text_edit(&mut self.edit) + } + pub fn create_file(&mut self, dst: AnchoredPathBuf, content: impl Into) { + let file_system_edit = FileSystemEdit::CreateFile { dst, initial_contents: content.into() }; + self.source_change.push_file_system_edit(file_system_edit); + } + pub fn move_file(&mut self, src: FileId, dst: AnchoredPathBuf) { + let file_system_edit = FileSystemEdit::MoveFile { src, dst }; + self.source_change.push_file_system_edit(file_system_edit); + } + pub fn trigger_signature_help(&mut self) { + self.trigger_signature_help = true; + } + + pub fn finish(mut self) -> SourceChange { + self.commit(); + mem::take(&mut self.source_change) + } +} + #[derive(Debug, Clone)] pub enum FileSystemEdit { CreateFile { dst: AnchoredPathBuf, initial_contents: String }, diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index e221425edd5b..9b9e21a4ddb5 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -15,6 +15,7 @@ itertools = "0.10.3" either = "1.7.0" +serde_json = "1.0.82" profile = { path = "../profile", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" } diff --git a/crates/ide-diagnostics/src/handlers/inactive_code.rs b/crates/ide-diagnostics/src/handlers/inactive_code.rs index 97ea5c456a63..5694f33525e0 100644 --- a/crates/ide-diagnostics/src/handlers/inactive_code.rs +++ b/crates/ide-diagnostics/src/handlers/inactive_code.rs @@ -43,7 +43,7 @@ mod tests { use crate::{tests::check_diagnostics_with_config, DiagnosticsConfig}; pub(crate) fn check(ra_fixture: &str) { - let config = DiagnosticsConfig::default(); + let config = DiagnosticsConfig::test_sample(); check_diagnostics_with_config(config, ra_fixture) } diff --git a/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs new file mode 100644 index 000000000000..a21db5b2cec9 --- /dev/null +++ b/crates/ide-diagnostics/src/handlers/json_is_not_rust.rs @@ -0,0 +1,310 @@ +//! This diagnostic provides an assist for creating a struct definition from a JSON +//! example. + +use hir::{PathResolution, Semantics}; +use ide_db::{ + base_db::FileId, + helpers::mod_path_to_ast, + imports::insert_use::{insert_use, ImportScope}, + source_change::SourceChangeBuilder, + RootDatabase, +}; +use itertools::Itertools; +use stdx::{format_to, never}; +use syntax::{ + ast::{self, make}, + SyntaxKind, SyntaxNode, +}; +use text_edit::TextEdit; + +use crate::{fix, Diagnostic, DiagnosticsConfig, Severity}; + +#[derive(Default)] +struct State { + result: String, + struct_counts: usize, + has_serialize: bool, + has_deserialize: bool, +} + +impl State { + fn generate_new_name(&mut self) -> ast::Name { + self.struct_counts += 1; + make::name(&format!("Struct{}", self.struct_counts)) + } + + fn serde_derive(&self) -> String { + let mut v = vec![]; + if self.has_serialize { + v.push("Serialize"); + } + if self.has_deserialize { + v.push("Deserialize"); + } + match v.as_slice() { + [] => "".to_string(), + [x] => format!("#[derive({x})]\n"), + [x, y] => format!("#[derive({x}, {y})]\n"), + _ => { + never!(); + "".to_string() + } + } + } + + fn build_struct(&mut self, value: &serde_json::Map) -> ast::Type { + let name = self.generate_new_name(); + let ty = make::ty(&name.to_string()); + let strukt = make::struct_( + None, + name, + None, + make::record_field_list(value.iter().sorted_unstable_by_key(|x| x.0).map( + |(name, value)| make::record_field(None, make::name(name), self.type_of(value)), + )) + .into(), + ); + format_to!(self.result, "{}{}\n", self.serde_derive(), strukt); + ty + } + + fn type_of(&mut self, value: &serde_json::Value) -> ast::Type { + match value { + serde_json::Value::Null => make::ty_unit(), + serde_json::Value::Bool(_) => make::ty("bool"), + serde_json::Value::Number(it) => make::ty(if it.is_i64() { "i64" } else { "f64" }), + serde_json::Value::String(_) => make::ty("String"), + serde_json::Value::Array(it) => { + let ty = match it.iter().next() { + Some(x) => self.type_of(x), + None => make::ty_placeholder(), + }; + make::ty(&format!("Vec<{ty}>")) + } + serde_json::Value::Object(x) => self.build_struct(x), + } + } +} + +pub(crate) fn json_in_items( + sema: &Semantics<'_, RootDatabase>, + acc: &mut Vec, + file_id: FileId, + node: &SyntaxNode, + config: &DiagnosticsConfig, +) { + (|| { + if node.kind() == SyntaxKind::ERROR + && node.first_token().map(|x| x.kind()) == Some(SyntaxKind::L_CURLY) + && node.last_token().map(|x| x.kind()) == Some(SyntaxKind::R_CURLY) + { + let node_string = node.to_string(); + if let Ok(it) = serde_json::from_str(&node_string) { + if let serde_json::Value::Object(it) = it { + let import_scope = ImportScope::find_insert_use_container(node, sema)?; + let range = node.text_range(); + let mut edit = TextEdit::builder(); + edit.delete(range); + let mut state = State::default(); + let semantics_scope = sema.scope(node)?; + let scope_resolve = + |it| semantics_scope.speculative_resolve(&make::path_from_text(it)); + let scope_has = |it| scope_resolve(it).is_some(); + let deserialize_resolved = scope_resolve("::serde::Deserialize"); + let serialize_resolved = scope_resolve("::serde::Serialize"); + state.has_deserialize = deserialize_resolved.is_some(); + state.has_serialize = serialize_resolved.is_some(); + state.build_struct(&it); + edit.insert(range.start(), state.result); + acc.push( + Diagnostic::new( + "json-is-not-rust", + "JSON syntax is not valid as a Rust item", + range, + ) + .severity(Severity::WeakWarning) + .with_fixes(Some(vec![{ + let mut scb = SourceChangeBuilder::new(file_id); + let scope = match import_scope.clone() { + ImportScope::File(it) => ImportScope::File(scb.make_mut(it)), + ImportScope::Module(it) => ImportScope::Module(scb.make_mut(it)), + ImportScope::Block(it) => ImportScope::Block(scb.make_mut(it)), + }; + let current_module = semantics_scope.module(); + if !scope_has("Serialize") { + if let Some(PathResolution::Def(it)) = serialize_resolved { + if let Some(it) = current_module.find_use_path_prefixed( + sema.db, + it, + config.insert_use.prefix_kind, + ) { + insert_use( + &scope, + mod_path_to_ast(&it), + &config.insert_use, + ); + } + } + } + if !scope_has("Deserialize") { + if let Some(PathResolution::Def(it)) = deserialize_resolved { + if let Some(it) = current_module.find_use_path_prefixed( + sema.db, + it, + config.insert_use.prefix_kind, + ) { + insert_use( + &scope, + mod_path_to_ast(&it), + &config.insert_use, + ); + } + } + } + let mut sc = scb.finish(); + sc.insert_source_edit(file_id, edit.finish()); + fix("convert_json_to_struct", "Convert JSON to struct", sc, range) + }])), + ); + } + } + } + Some(()) + })(); +} + +#[cfg(test)] +mod tests { + use crate::{ + tests::{check_diagnostics_with_config, check_fix, check_no_fix}, + DiagnosticsConfig, + }; + + #[test] + fn diagnostic_for_simple_case() { + let mut config = DiagnosticsConfig::test_sample(); + config.disabled.insert("syntax-error".to_string()); + check_diagnostics_with_config( + config, + r#" + { "foo": "bar" } + // ^^^^^^^^^^^^^^^^ 💡 weak: JSON syntax is not valid as a Rust item +"#, + ); + } + + #[test] + fn types_of_primitives() { + check_fix( + r#" + //- /lib.rs crate:lib deps:serde + use serde::Serialize; + + fn some_garbage() { + + } + + {$0 + "foo": "bar", + "bar": 2.3, + "baz": null, + "bay": 57, + "box": true + } + //- /serde.rs crate:serde + + pub trait Serialize { + fn serialize() -> u8; + } + "#, + r#" + use serde::Serialize; + + fn some_garbage() { + + } + + #[derive(Serialize)] + struct Struct1{ bar: f64, bay: i64, baz: (), r#box: bool, foo: String } + + "#, + ); + } + + #[test] + fn nested_structs() { + check_fix( + r#" + {$0 + "foo": "bar", + "bar": { + "kind": "Object", + "value": {} + } + } + "#, + r#" + struct Struct3{ } + struct Struct2{ kind: String, value: Struct3 } + struct Struct1{ bar: Struct2, foo: String } + + "#, + ); + } + + #[test] + fn arrays() { + check_fix( + r#" + //- /lib.rs crate:lib deps:serde + { + "of_string": ["foo", "2", "x"], $0 + "of_object": [{ + "x": 10, + "y": 20 + }, { + "x": 10, + "y": 20 + }], + "nested": [[[2]]], + "empty": [] + } + //- /serde.rs crate:serde + + pub trait Serialize { + fn serialize() -> u8; + } + pub trait Deserialize { + fn deserialize() -> u8; + } + "#, + r#" + use serde::Serialize; + use serde::Deserialize; + + #[derive(Serialize, Deserialize)] + struct Struct2{ x: i64, y: i64 } + #[derive(Serialize, Deserialize)] + struct Struct1{ empty: Vec<_>, nested: Vec>>, of_object: Vec, of_string: Vec } + + "#, + ); + } + + #[test] + fn no_emit_outside_of_item_position() { + check_no_fix( + r#" + fn foo() { + let json = {$0 + "foo": "bar", + "bar": { + "kind": "Object", + "value": {} + } + }; + } + "#, + ); + } +} diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index d6a66dc15091..43ff4ed5a6c8 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -79,7 +79,7 @@ pub macro panic { #[test] fn include_macro_should_allow_empty_content() { - let mut config = DiagnosticsConfig::default(); + let mut config = DiagnosticsConfig::test_sample(); // FIXME: This is a false-positive, the file is actually linked in via // `include!` macro diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index e032c578f028..a80299106bd3 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -68,7 +68,7 @@ fn missing_record_expr_field_fixes( } let new_field = make::record_field( None, - make::name(&record_expr_field.field_name()?.text()), + make::name(&record_expr_field.field_name()?.ident_token()?.text()), make::ty(&new_field_type.display_source_code(sema.db, module.into()).ok()?), ); @@ -109,7 +109,7 @@ fn missing_record_expr_field_fixes( #[cfg(test)] mod tests { - use crate::tests::{check_diagnostics, check_fix}; + use crate::tests::{check_diagnostics, check_fix, check_no_fix}; #[test] fn no_such_field_diagnostics() { @@ -277,6 +277,20 @@ struct Foo { bar: i32, pub(crate) baz: bool } +"#, + ) + } + + #[test] + fn test_tuple_field_on_record_struct() { + check_no_fix( + r#" +struct Struct {} +fn main() { + Struct { + 0$0: 0 + } +} "#, ) } diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 41abaa836f5f..61e63ea7a93c 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -50,6 +50,7 @@ mod handlers { pub(crate) mod field_shorthand; pub(crate) mod useless_braces; pub(crate) mod unlinked_file; + pub(crate) mod json_is_not_rust; } #[cfg(test)] @@ -59,6 +60,7 @@ use hir::{diagnostics::AnyDiagnostic, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::{FileId, FileRange, SourceDatabase}, + imports::insert_use::InsertUseConfig, label::Label, source_change::SourceChange, FxHashSet, RootDatabase, @@ -139,13 +141,37 @@ impl Default for ExprFillDefaultMode { } } -#[derive(Default, Debug, Clone)] +#[derive(Debug, Clone)] pub struct DiagnosticsConfig { pub proc_macros_enabled: bool, pub proc_attr_macros_enabled: bool, pub disable_experimental: bool, pub disabled: FxHashSet, pub expr_fill_default: ExprFillDefaultMode, + // FIXME: We may want to include a whole `AssistConfig` here + pub insert_use: InsertUseConfig, +} + +impl DiagnosticsConfig { + pub fn test_sample() -> Self { + use hir::PrefixKind; + use ide_db::imports::insert_use::ImportGranularity; + + Self { + proc_macros_enabled: Default::default(), + proc_attr_macros_enabled: Default::default(), + disable_experimental: Default::default(), + disabled: Default::default(), + expr_fill_default: Default::default(), + insert_use: InsertUseConfig { + granularity: ImportGranularity::Preserve, + enforce_granularity: false, + prefix_kind: PrefixKind::Plain, + group: false, + skip_glob_imports: false, + }, + } + } } struct DiagnosticsContext<'a> { @@ -172,9 +198,12 @@ pub fn diagnostics( }), ); - for node in parse.tree().syntax().descendants() { + let parse = sema.parse(file_id); + + for node in parse.syntax().descendants() { handlers::useless_braces::useless_braces(&mut res, file_id, &node); handlers::field_shorthand::field_shorthand(&mut res, file_id, &node); + handlers::json_is_not_rust::json_in_items(&sema, &mut res, file_id, &node, &config); } let module = sema.to_module_def(file_id); diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs index 7312bca32fed..729619cfde03 100644 --- a/crates/ide-diagnostics/src/tests.rs +++ b/crates/ide-diagnostics/src/tests.rs @@ -37,7 +37,7 @@ fn check_nth_fix(nth: usize, ra_fixture_before: &str, ra_fixture_after: &str) { let after = trim_indent(ra_fixture_after); let (db, file_position) = RootDatabase::with_position(ra_fixture_before); - let mut conf = DiagnosticsConfig::default(); + let mut conf = DiagnosticsConfig::test_sample(); conf.expr_fill_default = ExprFillDefaultMode::Default; let diagnostic = super::diagnostics(&db, &conf, &AssistResolveStrategy::All, file_position.file_id) @@ -69,7 +69,7 @@ pub(crate) fn check_no_fix(ra_fixture: &str) { let (db, file_position) = RootDatabase::with_position(ra_fixture); let diagnostic = super::diagnostics( &db, - &DiagnosticsConfig::default(), + &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, file_position.file_id, ) @@ -82,7 +82,7 @@ pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { let (db, file_id) = RootDatabase::with_single_file(ra_fixture); let diagnostics = super::diagnostics( &db, - &DiagnosticsConfig::default(), + &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, file_id, ); @@ -91,7 +91,7 @@ pub(crate) fn check_expect(ra_fixture: &str, expect: Expect) { #[track_caller] pub(crate) fn check_diagnostics(ra_fixture: &str) { - let mut config = DiagnosticsConfig::default(); + let mut config = DiagnosticsConfig::test_sample(); config.disabled.insert("inactive-code".to_string()); check_diagnostics_with_config(config, ra_fixture) } @@ -127,7 +127,7 @@ pub(crate) fn check_diagnostics_with_config(config: DiagnosticsConfig, ra_fixtur #[test] fn test_disabled_diagnostics() { - let mut config = DiagnosticsConfig::default(); + let mut config = DiagnosticsConfig::test_sample(); config.disabled.insert("unresolved-module".into()); let (db, file_id) = RootDatabase::with_single_file(r#"mod foo;"#); @@ -137,7 +137,7 @@ fn test_disabled_diagnostics() { let diagnostics = super::diagnostics( &db, - &DiagnosticsConfig::default(), + &DiagnosticsConfig::test_sample(), &AssistResolveStrategy::All, file_id, ); diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b2123b9a8793..d6cd5783f05e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1,4 +1,4 @@ -use std::{convert::TryInto, mem::discriminant}; +use std::mem::discriminant; use crate::{doc_links::token_as_doc_comment, FilePosition, NavigationTarget, RangeInfo, TryToNav}; use hir::{AsAssocItem, AssocItem, Semantics}; diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index b0853b10fde2..0181c6b8e456 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2225,4 +2225,190 @@ macro_rules! foo { "#]], ); } + + #[test] + fn test_paths_with_raw_ident() { + check( + r#" +//- /lib.rs +$0 +mod r#mod { + #[test] + fn r#fn() {} + + /// ``` + /// ``` + fn r#for() {} + + /// ``` + /// ``` + struct r#struct(r#type); + + /// ``` + /// ``` + impl r#struct { + /// ``` + /// ``` + fn r#fn() {} + } + + enum r#enum {} + impl r#struct { + /// ``` + /// ``` + fn r#fn() {} + } + + trait r#trait {} + + /// ``` + /// ``` + impl r#trait for r#struct {} +} +"#, + &[TestMod, Test, DocTest, DocTest, DocTest, DocTest, DocTest, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..461, + focus_range: 5..10, + name: "r#mod", + kind: Module, + description: "mod r#mod", + }, + kind: TestMod { + path: "r#mod", + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 17..41, + focus_range: 32..36, + name: "r#fn", + kind: Function, + }, + kind: Test { + test_id: Path( + "r#mod::r#fn", + ), + attr: TestAttr { + ignore: false, + }, + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 47..84, + name: "r#for", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#for", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 90..146, + name: "r#struct", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 152..266, + focus_range: 189..205, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 216..260, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 323..367, + name: "r#fn", + }, + kind: DocTest { + test_id: Path( + "r#mod::r#struct::r#fn", + ), + }, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 401..459, + focus_range: 445..456, + name: "impl", + kind: Impl, + }, + kind: DocTest { + test_id: Path( + "r#struct", + ), + }, + cfg: None, + }, + ] + "#]], + ) + } } diff --git a/crates/ide/src/status.rs b/crates/ide/src/status.rs index 32e39f82a0e9..f4d0387440d4 100644 --- a/crates/ide/src/status.rs +++ b/crates/ide/src/status.rs @@ -1,4 +1,4 @@ -use std::{fmt, iter::FromIterator, sync::Arc}; +use std::{fmt, sync::Arc}; use hir::{ExpandResult, MacroFile}; use ide_db::base_db::{ diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index aca6ecd424e4..e4c56565b92d 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -228,16 +228,7 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } let spacing = match conv.peek().map(|next| next.kind(conv)) { - Some(kind) - if !kind.is_trivia() - && kind.is_punct() - && kind != T!['['] - && kind != T!['{'] - && kind != T!['('] - && kind != UNDERSCORE => - { - tt::Spacing::Joint - } + Some(kind) if !kind.is_trivia() => tt::Spacing::Joint, _ => tt::Spacing::Alone, }; let char = match token.to_char(conv) { diff --git a/crates/parser/src/grammar/expressions.rs b/crates/parser/src/grammar/expressions.rs index e7402104eb83..dcaceade652a 100644 --- a/crates/parser/src/grammar/expressions.rs +++ b/crates/parser/src/grammar/expressions.rs @@ -564,8 +564,10 @@ fn path_expr(p: &mut Parser<'_>, r: Restrictions) -> (CompletedMarker, BlockLike // test record_lit // fn foo() { // S {}; +// S { x }; // S { x, y: 32, }; // S { x, y: 32, ..Default::default() }; +// S { x: ::default() }; // TupleStruct { 0: 1 }; // } pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { @@ -582,16 +584,26 @@ pub(crate) fn record_expr_field_list(p: &mut Parser<'_>) { match p.current() { IDENT | INT_NUMBER => { - // test_err record_literal_before_ellipsis_recovery + // test_err record_literal_missing_ellipsis_recovery // fn main() { - // S { field ..S::default() } + // S { S::default() } // } - if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) { - name_ref_or_index(p); - p.expect(T![:]); + if p.nth_at(1, T![::]) { + m.abandon(p); + p.expect(T![..]); + expr(p); + } else { + // test_err record_literal_before_ellipsis_recovery + // fn main() { + // S { field ..S::default() } + // } + if p.nth_at(1, T![:]) || p.nth_at(1, T![..]) { + name_ref_or_index(p); + p.expect(T![:]); + } + expr(p); + m.complete(p, RECORD_EXPR_FIELD); } - expr(p); - m.complete(p, RECORD_EXPR_FIELD); } T![.] if p.at(T![..]) => { m.abandon(p); diff --git a/crates/parser/src/grammar/patterns.rs b/crates/parser/src/grammar/patterns.rs index 4cbf10306149..7e21a808da0a 100644 --- a/crates/parser/src/grammar/patterns.rs +++ b/crates/parser/src/grammar/patterns.rs @@ -75,6 +75,16 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { // Some(1..) => () // } // + // match () { + // S { a: 0 } => (), + // S { a: 1.. } => (), + // } + // + // match () { + // [0] => (), + // [1..] => (), + // } + // // match (10 as u8, 5 as u8) { // (0, _) => (), // (1.., _) => () @@ -88,11 +98,27 @@ fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) { let m = lhs.precede(p); p.bump(range_op); - // `0 .. =>` or `let 0 .. =` or `Some(0 .. )` - // ^ ^ ^ - if p.at(T![=]) | p.at(T![')']) | p.at(T![,]) { + // testing if we're at one of the following positions: + // `0 .. =>` + // ^ + // `let 0 .. =` + // ^ + // `let 0..: _ =` + // ^ + // (1.., _) + // ^ + // `Some(0 .. )` + // ^ + // `S { t: 0.. }` + // ^ + // `[0..]` + // ^ + if matches!(p.current(), T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']']) { // test half_open_range_pat - // fn f() { let 0 .. = 1u32; } + // fn f() { + // let 0 .. = 1u32; + // let 0..: _ = 1u32; + // } } else { atom_pat(p, recovery_set); } diff --git a/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast b/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast new file mode 100644 index 000000000000..0c5b618e6f05 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rast @@ -0,0 +1,43 @@ +SOURCE_FILE + FN + FN_KW "fn" + WHITESPACE " " + NAME + IDENT "main" + PARAM_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + BLOCK_EXPR + STMT_LIST + L_CURLY "{" + WHITESPACE "\n " + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + COLON2 "::" + PATH_SEGMENT + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + WHITESPACE "\n" + R_CURLY "}" + WHITESPACE "\n" +error 19: expected DOT2 diff --git a/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs b/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs new file mode 100644 index 000000000000..1b594e8ab962 --- /dev/null +++ b/crates/parser/test_data/parser/inline/err/0014_record_literal_missing_ellipsis_recovery.rs @@ -0,0 +1,3 @@ +fn main() { + S { S::default() } +} diff --git a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast index 44c967e8dc59..cfef5d3f9538 100644 --- a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast +++ b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rast @@ -172,6 +172,122 @@ SOURCE_FILE WHITESPACE "\n " R_CURLY "}" WHITESPACE "\n\n " + EXPR_STMT + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + RECORD_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_PAT_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_PAT_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + LITERAL_PAT + LITERAL + INT_NUMBER "0" + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + RECORD_PAT + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_PAT_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_PAT_FIELD + NAME_REF + IDENT "a" + COLON ":" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "1" + DOT2 ".." + WHITESPACE " " + R_CURLY "}" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n\n " + EXPR_STMT + MATCH_EXPR + MATCH_KW "match" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + MATCH_ARM_LIST + L_CURLY "{" + WHITESPACE "\n " + MATCH_ARM + SLICE_PAT + L_BRACK "[" + LITERAL_PAT + LITERAL + INT_NUMBER "0" + R_BRACK "]" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + MATCH_ARM + SLICE_PAT + L_BRACK "[" + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "1" + DOT2 ".." + R_BRACK "]" + WHITESPACE " " + FAT_ARROW "=>" + WHITESPACE " " + TUPLE_EXPR + L_PAREN "(" + R_PAREN ")" + COMMA "," + WHITESPACE "\n " + R_CURLY "}" + WHITESPACE "\n\n " MATCH_EXPR MATCH_KW "match" WHITESPACE " " diff --git a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs index 6c586a895609..2411d51096b3 100644 --- a/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs +++ b/crates/parser/test_data/parser/inline/ok/0058_range_pat.rs @@ -11,6 +11,16 @@ fn main() { Some(1..) => () } + match () { + S { a: 0 } => (), + S { a: 1.. } => (), + } + + match () { + [0] => (), + [1..] => (), + } + match (10 as u8, 5 as u8) { (0, _) => (), (1.., _) => () diff --git a/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast b/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast index 9997d0ae348c..00948c322f4c 100644 --- a/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast +++ b/crates/parser/test_data/parser/inline/ok/0061_record_lit.rast @@ -24,6 +24,26 @@ SOURCE_FILE R_CURLY "}" SEMICOLON ";" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + PATH_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "x" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " EXPR_STMT RECORD_EXPR PATH @@ -100,6 +120,35 @@ SOURCE_FILE R_CURLY "}" SEMICOLON ";" WHITESPACE "\n " + EXPR_STMT + RECORD_EXPR + PATH + PATH_SEGMENT + NAME_REF + IDENT "S" + WHITESPACE " " + RECORD_EXPR_FIELD_LIST + L_CURLY "{" + WHITESPACE " " + RECORD_EXPR_FIELD + NAME_REF + IDENT "x" + COLON ":" + WHITESPACE " " + CALL_EXPR + PATH_EXPR + PATH + PATH_SEGMENT + COLON2 "::" + NAME_REF + IDENT "default" + ARG_LIST + L_PAREN "(" + R_PAREN ")" + WHITESPACE " " + R_CURLY "}" + SEMICOLON ";" + WHITESPACE "\n " EXPR_STMT RECORD_EXPR PATH diff --git a/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs b/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs index 6285e5549772..86411fbb7dc0 100644 --- a/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs +++ b/crates/parser/test_data/parser/inline/ok/0061_record_lit.rs @@ -1,6 +1,8 @@ fn foo() { S {}; + S { x }; S { x, y: 32, }; S { x, y: 32, ..Default::default() }; + S { x: ::default() }; TupleStruct { 0: 1 }; } diff --git a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast index 3d3587a70633..4b401b60df0c 100644 --- a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast +++ b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rast @@ -11,7 +11,7 @@ SOURCE_FILE BLOCK_EXPR STMT_LIST L_CURLY "{" - WHITESPACE " " + WHITESPACE "\n " LET_STMT LET_KW "let" WHITESPACE " " @@ -27,6 +27,25 @@ SOURCE_FILE LITERAL INT_NUMBER "1u32" SEMICOLON ";" - WHITESPACE " " + WHITESPACE "\n " + LET_STMT + LET_KW "let" + WHITESPACE " " + RANGE_PAT + LITERAL_PAT + LITERAL + INT_NUMBER "0" + DOT2 ".." + COLON ":" + WHITESPACE " " + INFER_TYPE + UNDERSCORE "_" + WHITESPACE " " + EQ "=" + WHITESPACE " " + LITERAL + INT_NUMBER "1u32" + SEMICOLON ";" + WHITESPACE "\n" R_CURLY "}" WHITESPACE "\n" diff --git a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs index 1360eda05658..c9386a221a95 100644 --- a/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs +++ b/crates/parser/test_data/parser/inline/ok/0166_half_open_range_pat.rs @@ -1 +1,4 @@ -fn f() { let 0 .. = 1u32; } +fn f() { + let 0 .. = 1u32; + let 0..: _ = 1u32; +} diff --git a/crates/proc-macro-api/src/msg/flat.rs b/crates/proc-macro-api/src/msg/flat.rs index 8437444e183a..268a03bb5359 100644 --- a/crates/proc-macro-api/src/msg/flat.rs +++ b/crates/proc-macro-api/src/msg/flat.rs @@ -35,10 +35,7 @@ //! as we don't have bincode in Cargo.toml yet, lets stick with serde_json for //! the time being. -use std::{ - collections::{HashMap, VecDeque}, - convert::TryInto, -}; +use std::collections::{HashMap, VecDeque}; use serde::{Deserialize, Serialize}; use tt::TokenId; diff --git a/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/mod.rs index 4a07f22779e6..a405497f3c9b 100644 --- a/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/mod.rs +++ b/crates/proc-macro-srv/src/abis/abi_1_58/proc_macro/mod.rs @@ -157,7 +157,7 @@ impl From for TokenStream { } /// Collects a number of token trees into a single stream. -impl iter::FromIterator for TokenStream { +impl FromIterator for TokenStream { fn from_iter>(trees: I) -> Self { trees.into_iter().map(TokenStream::from).collect() } @@ -165,7 +165,7 @@ impl iter::FromIterator for TokenStream { /// A "flattening" operation on token streams, collects token trees /// from multiple token streams into a single stream. -impl iter::FromIterator for TokenStream { +impl FromIterator for TokenStream { fn from_iter>(streams: I) -> Self { let mut builder = bridge::client::TokenStreamBuilder::new(); streams.into_iter().for_each(|stream| builder.push(stream.0)); diff --git a/crates/proc-macro-srv/src/abis/abi_1_58/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_1_58/ra_server.rs index ebdfca00d735..b1e982f4779f 100644 --- a/crates/proc-macro-srv/src/abis/abi_1_58/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_1_58/ra_server.rs @@ -12,7 +12,6 @@ use super::proc_macro::bridge::{self, server}; use std::collections::HashMap; use std::hash::Hash; -use std::iter::FromIterator; use std::ops::Bound; use std::{ascii, vec::IntoIter}; diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs index c50a16bf4d1d..7ab1f421daf8 100644 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs +++ b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs @@ -207,7 +207,7 @@ impl ConcatStreamsHelper { } /// Collects a number of token trees into a single stream. -impl iter::FromIterator for TokenStream { +impl FromIterator for TokenStream { fn from_iter>(trees: I) -> Self { trees.into_iter().map(TokenStream::from).collect() } @@ -215,7 +215,7 @@ impl iter::FromIterator for TokenStream { /// A "flattening" operation on token streams, collects token trees /// from multiple token streams into a single stream. -impl iter::FromIterator for TokenStream { +impl FromIterator for TokenStream { fn from_iter>(streams: I) -> Self { let iter = streams.into_iter(); let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs index 05a565fbf343..ed49cc759660 100644 --- a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs @@ -12,7 +12,6 @@ use super::proc_macro::bridge::{self, server}; use std::collections::HashMap; use std::hash::Hash; -use std::iter::FromIterator; use std::ops::Bound; use std::{ascii, vec::IntoIter}; diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_64/mod.rs deleted file mode 100644 index 9d56f0eaf888..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/mod.rs +++ /dev/null @@ -1,105 +0,0 @@ -//! Proc macro ABI. - -#[allow(dead_code)] -#[doc(hidden)] -mod proc_macro; - -#[allow(dead_code)] -#[doc(hidden)] -mod ra_server; - -use libloading::Library; -use proc_macro_api::ProcMacroKind; - -use super::PanicMessage; - -pub use ra_server::TokenStream; - -pub(crate) struct Abi { - exported_macros: Vec, -} - -impl From for PanicMessage { - fn from(p: proc_macro::bridge::PanicMessage) -> Self { - Self { message: p.as_str().map(|s| s.to_string()) } - } -} - -impl Abi { - pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result { - let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> = - lib.get(symbol_name.as_bytes())?; - Ok(Self { exported_macros: macros.to_vec() }) - } - - pub fn expand( - &self, - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - let parsed_body = TokenStream::with_subtree(macro_body.clone()); - - let parsed_attributes = - attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone())); - - for proc_macro in &self.exported_macros { - match proc_macro { - proc_macro::bridge::client::ProcMacro::CustomDerive { - trait_name, client, .. - } if *trait_name == macro_name => { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - proc_macro::bridge::client::ProcMacro::Bang { name, client } - if *name == macro_name => - { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - proc_macro::bridge::client::ProcMacro::Attr { name, client } - if *name == macro_name => - { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_attributes, - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - _ => continue, - } - } - - Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into()) - } - - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.exported_macros - .iter() - .map(|proc_macro| match proc_macro { - proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - proc_macro::bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - proc_macro::bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/buffer.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/buffer.rs deleted file mode 100644 index 48030f8d82dc..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/buffer.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Buffer management for same-process client<->server communication. - -use std::io::{self, Write}; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::slice; - -#[repr(C)] -pub struct Buffer { - data: *mut u8, - len: usize, - capacity: usize, - reserve: extern "C" fn(Buffer, usize) -> Buffer, - drop: extern "C" fn(Buffer), -} - -unsafe impl Sync for Buffer {} -unsafe impl Send for Buffer {} - -impl Default for Buffer { - #[inline] - fn default() -> Self { - Self::from(vec![]) - } -} - -impl Deref for Buffer { - type Target = [u8]; - #[inline] - fn deref(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data as *const u8, self.len) } - } -} - -impl DerefMut for Buffer { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.data, self.len) } - } -} - -impl Buffer { - #[inline] - pub(super) fn new() -> Self { - Self::default() - } - - #[inline] - pub(super) fn clear(&mut self) { - self.len = 0; - } - - #[inline] - pub(super) fn take(&mut self) -> Self { - mem::take(self) - } - - // We have the array method separate from extending from a slice. This is - // because in the case of small arrays, codegen can be more efficient - // (avoiding a memmove call). With extend_from_slice, LLVM at least - // currently is not able to make that optimization. - #[inline] - pub(super) fn extend_from_array(&mut self, xs: &[u8; N]) { - if xs.len() > (self.capacity - self.len) { - let b = self.take(); - *self = (b.reserve)(b, xs.len()); - } - unsafe { - xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); - self.len += xs.len(); - } - } - - #[inline] - pub(super) fn extend_from_slice(&mut self, xs: &[u8]) { - if xs.len() > (self.capacity - self.len) { - let b = self.take(); - *self = (b.reserve)(b, xs.len()); - } - unsafe { - xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); - self.len += xs.len(); - } - } - - #[inline] - pub(super) fn push(&mut self, v: u8) { - // The code here is taken from Vec::push, and we know that reserve() - // will panic if we're exceeding isize::MAX bytes and so there's no need - // to check for overflow. - if self.len == self.capacity { - let b = self.take(); - *self = (b.reserve)(b, 1); - } - unsafe { - *self.data.add(self.len) = v; - self.len += 1; - } - } -} - -impl Write for Buffer { - #[inline] - fn write(&mut self, xs: &[u8]) -> io::Result { - self.extend_from_slice(xs); - Ok(xs.len()) - } - - #[inline] - fn write_all(&mut self, xs: &[u8]) -> io::Result<()> { - self.extend_from_slice(xs); - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Drop for Buffer { - #[inline] - fn drop(&mut self) { - let b = self.take(); - (b.drop)(b); - } -} - -impl From> for Buffer { - fn from(mut v: Vec) -> Self { - let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); - mem::forget(v); - - // This utility function is nested in here because it can *only* - // be safely called on `Buffer`s created by *this* `proc_macro`. - fn to_vec(b: Buffer) -> Vec { - unsafe { - let Buffer { data, len, capacity, .. } = b; - mem::forget(b); - Vec::from_raw_parts(data, len, capacity) - } - } - - extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { - let mut v = to_vec(b); - v.reserve(additional); - Buffer::from(v) - } - - extern "C" fn drop(b: Buffer) { - mem::drop(to_vec(b)); - } - - Buffer { data, len, capacity, reserve, drop } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/client.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/client.rs deleted file mode 100644 index 22bda8ba5a7c..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/client.rs +++ /dev/null @@ -1,529 +0,0 @@ -//! Client-side types. - -use super::*; - -use std::marker::PhantomData; - -macro_rules! define_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[repr(C)] - #[allow(non_snake_case)] - pub struct HandleCounters { - $($oty: AtomicUsize,)* - $($ity: AtomicUsize,)* - } - - impl HandleCounters { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - extern "C" fn get() -> &'static Self { - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicUsize::new(1),)* - $($ity: AtomicUsize::new(1),)* - }; - &COUNTERS - } - } - - // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. - #[repr(C)] - #[allow(non_snake_case)] - pub(super) struct HandleStore { - $($oty: handle::OwnedStore,)* - $($ity: handle::InternedStore,)* - } - - impl HandleStore { - pub(super) fn new(handle_counters: &'static HandleCounters) -> Self { - HandleStore { - $($oty: handle::OwnedStore::new(&handle_counters.$oty),)* - $($ity: handle::InternedStore::new(&handle_counters.$ity),)* - } - } - } - - $( - #[repr(C)] - pub(crate) struct $oty { - handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, - } - - // Forward `Drop::drop` to the inherent `drop` method. - impl Drop for $oty { - fn drop(&mut self) { - $oty { - handle: self.handle, - _marker: PhantomData, - }.drop(); - } - } - - impl Encode for $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - let handle = self.handle; - mem::forget(self); - handle.encode(w, s); - } - } - - impl DecodeMut<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$oty.take(handle::Handle::decode(r, &mut ())) - } - } - - impl Encode for &$oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl<'s, S: server::Types> Decode<'_, 's, HandleStore>> - for &'s Marked - { - fn decode(r: &mut Reader<'_>, s: &'s HandleStore>) -> Self { - &s.$oty[handle::Handle::decode(r, &mut ())] - } - } - - impl Encode for &mut $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore>> - for &'s mut Marked - { - fn decode( - r: &mut Reader<'_>, - s: &'s mut HandleStore> - ) -> Self { - &mut s.$oty[handle::Handle::decode(r, &mut ())] - } - } - - impl Encode>> - for Marked - { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$oty.alloc(self).encode(w, s); - } - } - - impl DecodeMut<'_, '_, S> for $oty { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $oty { - handle: handle::Handle::decode(r, s), - _marker: PhantomData, - } - } - } - )* - - $( - #[repr(C)] - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - pub(crate) struct $ity { - handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, - } - - impl Encode for $ity { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl DecodeMut<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$ity.copy(handle::Handle::decode(r, &mut ())) - } - } - - impl Encode>> - for Marked - { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$ity.alloc(self).encode(w, s); - } - } - - impl DecodeMut<'_, '_, S> for $ity { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $ity { - handle: handle::Handle::decode(r, s), - _marker: PhantomData, - } - } - } - )* - } -} -define_handles! { - 'owned: - FreeFunctions, - TokenStream, - Literal, - SourceFile, - MultiSpan, - Diagnostic, - - 'interned: - Ident, - Span, -} - -// FIXME(eddyb) generate these impls by pattern-matching on the -// names of methods - also could use the presence of `fn drop` -// to distinguish between 'owned and 'interned, above. -// Alternatively, special "modes" could be listed of types in with_api -// instead of pattern matching on methods, here and in server decl. - -impl Clone for TokenStream { - fn clone(&self) -> Self { - self.clone() - } -} - -impl Clone for Literal { - fn clone(&self) -> Self { - self.clone() - } -} - -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Literal") - // format the kind without quotes, as in `kind: Float` - .field("kind", &format_args!("{}", &self.debug_kind())) - .field("symbol", &self.symbol()) - // format `Some("...")` on one line even in {:#?} mode - .field("suffix", &format_args!("{:?}", &self.suffix())) - .field("span", &self.span()) - .finish() - } -} - -impl Clone for SourceFile { - fn clone(&self) -> Self { - self.clone() - } -} - -impl Span { - pub(crate) fn def_site() -> Span { - Bridge::with(|bridge| bridge.globals.def_site) - } - - pub(crate) fn call_site() -> Span { - Bridge::with(|bridge| bridge.globals.call_site) - } - - pub(crate) fn mixed_site() -> Span { - Bridge::with(|bridge| bridge.globals.mixed_site) - } -} - -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.debug()) - } -} - -macro_rules! define_client_side { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $(impl $name { - $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { - Bridge::with(|bridge| { - let mut buf = bridge.cached_buffer.take(); - - buf.clear(); - api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); - reverse_encode!(buf; $($arg),*); - - buf = bridge.dispatch.call(buf); - - let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ()); - - bridge.cached_buffer = buf; - - r.unwrap_or_else(|e| panic::resume_unwind(e.into())) - }) - })* - })* - } -} -with_api!(self, self, define_client_side); - -struct Bridge<'a> { - /// Reusable buffer (only `clear`-ed, never shrunk), primarily - /// used for making requests. - cached_buffer: Buffer, - - /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, - - /// Provided globals for this macro expansion. - globals: ExpnGlobals, -} - -enum BridgeState<'a> { - /// No server is currently connected to this client. - NotConnected, - - /// A server is connected and available for requests. - Connected(Bridge<'a>), - - /// Access to the bridge is being exclusively acquired - /// (e.g., during `BridgeState::with`). - InUse, -} - -enum BridgeStateL {} - -impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL { - type Out = BridgeState<'a>; -} - -thread_local! { - static BRIDGE_STATE: scoped_cell::ScopedCell = - scoped_cell::ScopedCell::new(BridgeState::NotConnected); -} - -impl BridgeState<'_> { - /// Take exclusive control of the thread-local - /// `BridgeState`, and pass it to `f`, mutably. - /// The state will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - /// - /// N.B., while `f` is running, the thread-local state - /// is `BridgeState::InUse`. - fn with(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R { - BRIDGE_STATE.with(|state| { - state.replace(BridgeState::InUse, |mut state| { - // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone - f(&mut *state) - }) - }) - } -} - -impl Bridge<'_> { - fn with(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R { - BridgeState::with(|state| match state { - BridgeState::NotConnected => { - panic!("procedural macro API is used outside of a procedural macro"); - } - BridgeState::InUse => { - panic!("procedural macro API is used while it's already in use"); - } - BridgeState::Connected(bridge) => f(bridge), - }) - } -} - -pub(crate) fn is_available() -> bool { - BridgeState::with(|state| match state { - BridgeState::Connected(_) | BridgeState::InUse => true, - BridgeState::NotConnected => false, - }) -} - -/// A client-side RPC entry-point, which may be using a different `proc_macro` -/// from the one used by the server, but can be invoked compatibly. -/// -/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters -/// decorate the `Client` with the RPC "interface" of the entry-point, but -/// do not themselves participate in ABI, at all, only facilitate type-checking. -/// -/// E.g. `Client` is the common proc macro interface, -/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`, -/// indicating that the RPC input and output will be serialized token streams, -/// and forcing the use of APIs that take/return `S::TokenStream`, server-side. -#[repr(C)] -pub struct Client { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, - - pub(super) run: extern "C" fn(BridgeConfig<'_>) -> Buffer, - - pub(super) _marker: PhantomData O>, -} - -impl Copy for Client {} -impl Clone for Client { - fn clone(&self) -> Self { - *self - } -} - -fn maybe_install_panic_hook(force_show_panics: bool) { - // Hide the default panic output within `proc_macro` expansions. - // NB. the server can't do this because it may use a different libstd. - static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); - HIDE_PANICS_DURING_EXPANSION.call_once(|| { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { - let show = BridgeState::with(|state| match state { - BridgeState::NotConnected => true, - BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, - }); - if show { - prev(info) - } - })); - }); -} - -/// Client-side helper for handling client panics, entering the bridge, -/// deserializing input and serializing output. -// FIXME(eddyb) maybe replace `Bridge::enter` with this? -fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( - config: BridgeConfig<'_>, - f: impl FnOnce(A) -> R, -) -> Buffer { - let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config; - - panic::catch_unwind(panic::AssertUnwindSafe(|| { - maybe_install_panic_hook(force_show_panics); - - let reader = &mut &buf[..]; - let (globals, input) = <(ExpnGlobals, A)>::decode(reader, &mut ()); - - // Put the buffer we used for input back in the `Bridge` for requests. - let new_state = - BridgeState::Connected(Bridge { cached_buffer: buf.take(), dispatch, globals }); - - BRIDGE_STATE.with(|state| { - state.set(new_state, || { - let output = f(input); - - // Take the `cached_buffer` back out, for the output value. - buf = Bridge::with(|bridge| bridge.cached_buffer.take()); - - // HACK(eddyb) Separate encoding a success value (`Ok(output)`) - // from encoding a panic (`Err(e: PanicMessage)`) to avoid - // having handles outside the `bridge.enter(|| ...)` scope, and - // to catch panics that could happen while encoding the success. - // - // Note that panics should be impossible beyond this point, but - // this is defensively trying to avoid any accidental panicking - // reaching the `extern "C"` (which should `abort` but might not - // at the moment, so this is also potentially preventing UB). - buf.clear(); - Ok::<_, ()>(output).encode(&mut buf, &mut ()); - }) - }) - })) - .map_err(PanicMessage::from) - .unwrap_or_else(|e| { - buf.clear(); - Err::<(), _>(e).encode(&mut buf, &mut ()); - }); - buf -} - -impl Client { - pub const fn expand1( - f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - Client { - get_handle_counters: HandleCounters::get, - run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { - run_client(bridge, |input| f(super::super::TokenStream(input)).0) - }), - _marker: PhantomData, - } - } -} - -impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> { - pub const fn expand2( - f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream - + Copy, - ) -> Self { - Client { - get_handle_counters: HandleCounters::get, - run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { - run_client(bridge, |(input, input2)| { - f(super::super::TokenStream(input), super::super::TokenStream(input2)).0 - }) - }), - _marker: PhantomData, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum ProcMacro { - CustomDerive { - trait_name: &'static str, - attributes: &'static [&'static str], - client: Client, - }, - - Attr { - name: &'static str, - client: Client< - (super::super::TokenStream, super::super::TokenStream), - super::super::TokenStream, - >, - }, - - Bang { - name: &'static str, - client: Client, - }, -} - -impl ProcMacro { - pub fn name(&self) -> &'static str { - match self { - ProcMacro::CustomDerive { trait_name, .. } => trait_name, - ProcMacro::Attr { name, .. } => name, - ProcMacro::Bang { name, .. } => name, - } - } - - pub const fn custom_derive( - trait_name: &'static str, - attributes: &'static [&'static str], - expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } - } - - pub const fn attr( - name: &'static str, - expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream - + Copy, - ) -> Self { - ProcMacro::Attr { name, client: Client::expand2(expand) } - } - - pub const fn bang( - name: &'static str, - expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - ProcMacro::Bang { name, client: Client::expand1(expand) } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/closure.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/closure.rs deleted file mode 100644 index d371ae3cea09..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/closure.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. - -use std::marker::PhantomData; - -#[repr(C)] -pub struct Closure<'a, A, R> { - call: unsafe extern "C" fn(*mut Env, A) -> R, - env: *mut Env, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. - // - // The `'a` lifetime parameter represents the lifetime of `Env`. - _marker: PhantomData<*mut &'a mut ()>, -} - -struct Env; - -impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { - fn from(f: &'a mut F) -> Self { - unsafe extern "C" fn call R>(env: *mut Env, arg: A) -> R { - (*(env as *mut _ as *mut F))(arg) - } - Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } - } -} - -impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { - unsafe { (self.call)(self.env, arg) } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/handle.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/handle.rs deleted file mode 100644 index c219a9465d39..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/handle.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Server-side handles and storage for per-handle data. - -use std::collections::{BTreeMap, HashMap}; -use std::hash::{BuildHasher, Hash}; -use std::num::NonZeroU32; -use std::ops::{Index, IndexMut}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -pub(super) type Handle = NonZeroU32; - -/// A store that associates values of type `T` with numeric handles. A value can -/// be looked up using its handle. -pub(super) struct OwnedStore { - counter: &'static AtomicUsize, - data: BTreeMap, -} - -impl OwnedStore { - pub(super) fn new(counter: &'static AtomicUsize) -> Self { - // Ensure the handle counter isn't 0, which would panic later, - // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`. - assert_ne!(counter.load(Ordering::SeqCst), 0); - - OwnedStore { counter, data: BTreeMap::new() } - } -} - -impl OwnedStore { - pub(super) fn alloc(&mut self, x: T) -> Handle { - let counter = self.counter.fetch_add(1, Ordering::SeqCst); - let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed"); - assert!(self.data.insert(handle, x).is_none()); - handle - } - - pub(super) fn take(&mut self, h: Handle) -> T { - self.data.remove(&h).expect("use-after-free in `proc_macro` handle") - } -} - -impl Index for OwnedStore { - type Output = T; - fn index(&self, h: Handle) -> &T { - self.data.get(&h).expect("use-after-free in `proc_macro` handle") - } -} - -impl IndexMut for OwnedStore { - fn index_mut(&mut self, h: Handle) -> &mut T { - self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle") - } -} - -// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement -// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`). -#[derive(Clone)] -struct NonRandomState; - -impl BuildHasher for NonRandomState { - type Hasher = std::collections::hash_map::DefaultHasher; - #[inline] - fn build_hasher(&self) -> Self::Hasher { - Self::Hasher::new() - } -} - -/// Like `OwnedStore`, but avoids storing any value more than once. -pub(super) struct InternedStore { - owned: OwnedStore, - interner: HashMap, -} - -impl InternedStore { - pub(super) fn new(counter: &'static AtomicUsize) -> Self { - InternedStore { - owned: OwnedStore::new(counter), - interner: HashMap::with_hasher(NonRandomState), - } - } - - pub(super) fn alloc(&mut self, x: T) -> Handle { - let owned = &mut self.owned; - *self.interner.entry(x).or_insert_with(|| owned.alloc(x)) - } - - pub(super) fn copy(&mut self, h: Handle) -> T { - self.owned[h] - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/mod.rs deleted file mode 100644 index ffd440793231..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/mod.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! Internal interface for communicating between a `proc_macro` client -//! (a proc macro crate) and a `proc_macro` server (a compiler front-end). -//! -//! Serialization (with C ABI buffers) and unique integer handles are employed -//! to allow safely interfacing between two copies of `proc_macro` built -//! (from the same source) by different compilers with potentially mismatching -//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). - -#![deny(unsafe_code)] - -pub use super::{Delimiter, Level, LineColumn, Spacing}; -use std::fmt; -use std::hash::Hash; -use std::marker; -use std::mem; -use std::ops::Bound; -use std::panic; -use std::sync::atomic::AtomicUsize; -use std::sync::Once; -use std::thread; - -/// Higher-order macro describing the server RPC API, allowing automatic -/// generation of type-safe Rust APIs, both client-side and server-side. -/// -/// `with_api!(MySelf, my_self, my_macro)` expands to: -/// ```rust,ignore (pseudo-code) -/// my_macro! { -/// // ... -/// Literal { -/// // ... -/// fn character(ch: char) -> MySelf::Literal; -/// // ... -/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); -/// }, -/// // ... -/// } -/// ``` -/// -/// The first two arguments serve to customize the arguments names -/// and argument/return types, to enable several different usecases: -/// -/// If `my_self` is just `self`, then each `fn` signature can be used -/// as-is for a method. If it's anything else (`self_` in practice), -/// then the signatures don't have a special `self` argument, and -/// can, therefore, have a different one introduced. -/// -/// If `MySelf` is just `Self`, then the types are only valid inside -/// a trait or a trait impl, where the trait has associated types -/// for each of the API types. If non-associated types are desired, -/// a module name (`self` in practice) can be used instead of `Self`. -macro_rules! with_api { - ($S:ident, $self:ident, $m:ident) => { - $m! { - FreeFunctions { - fn drop($self: $S::FreeFunctions); - fn track_env_var(var: &str, value: Option<&str>); - fn track_path(path: &str); - }, - TokenStream { - fn drop($self: $S::TokenStream); - fn clone($self: &$S::TokenStream) -> $S::TokenStream; - fn is_empty($self: &$S::TokenStream) -> bool; - fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn from_str(src: &str) -> $S::TokenStream; - fn to_string($self: &$S::TokenStream) -> String; - fn from_token_tree( - tree: TokenTree<$S::TokenStream, $S::Span, $S::Ident, $S::Literal>, - ) -> $S::TokenStream; - fn concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; - fn concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; - fn into_trees( - $self: $S::TokenStream - ) -> Vec>; - }, - Ident { - fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident; - fn span($self: $S::Ident) -> $S::Span; - fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident; - }, - Literal { - fn drop($self: $S::Literal); - fn clone($self: &$S::Literal) -> $S::Literal; - fn from_str(s: &str) -> Result<$S::Literal, ()>; - fn to_string($self: &$S::Literal) -> String; - fn debug_kind($self: &$S::Literal) -> String; - fn symbol($self: &$S::Literal) -> String; - fn suffix($self: &$S::Literal) -> Option; - fn integer(n: &str) -> $S::Literal; - fn typed_integer(n: &str, kind: &str) -> $S::Literal; - fn float(n: &str) -> $S::Literal; - fn f32(n: &str) -> $S::Literal; - fn f64(n: &str) -> $S::Literal; - fn string(string: &str) -> $S::Literal; - fn character(ch: char) -> $S::Literal; - fn byte_string(bytes: &[u8]) -> $S::Literal; - fn span($self: &$S::Literal) -> $S::Span; - fn set_span($self: &mut $S::Literal, span: $S::Span); - fn subspan( - $self: &$S::Literal, - start: Bound, - end: Bound, - ) -> Option<$S::Span>; - }, - SourceFile { - fn drop($self: $S::SourceFile); - fn clone($self: &$S::SourceFile) -> $S::SourceFile; - fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool; - fn path($self: &$S::SourceFile) -> String; - fn is_real($self: &$S::SourceFile) -> bool; - }, - MultiSpan { - fn drop($self: $S::MultiSpan); - fn new() -> $S::MultiSpan; - fn push($self: &mut $S::MultiSpan, span: $S::Span); - }, - Diagnostic { - fn drop($self: $S::Diagnostic); - fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; - fn sub( - $self: &mut $S::Diagnostic, - level: Level, - msg: &str, - span: $S::MultiSpan, - ); - fn emit($self: $S::Diagnostic); - }, - Span { - fn debug($self: $S::Span) -> String; - fn source_file($self: $S::Span) -> $S::SourceFile; - fn parent($self: $S::Span) -> Option<$S::Span>; - fn source($self: $S::Span) -> $S::Span; - fn start($self: $S::Span) -> LineColumn; - fn end($self: $S::Span) -> LineColumn; - fn before($self: $S::Span) -> $S::Span; - fn after($self: $S::Span) -> $S::Span; - fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; - fn source_text($self: $S::Span) -> Option; - fn save_span($self: $S::Span) -> usize; - fn recover_proc_macro_span(id: usize) -> $S::Span; - }, - } - }; -} - -// FIXME(eddyb) this calls `encode` for each argument, but in reverse, -// to match the ordering in `reverse_decode`. -macro_rules! reverse_encode { - ($writer:ident;) => {}; - ($writer:ident; $first:ident $(, $rest:ident)*) => { - reverse_encode!($writer; $($rest),*); - $first.encode(&mut $writer, &mut ()); - } -} - -// FIXME(eddyb) this calls `decode` for each argument, but in reverse, -// to avoid borrow conflicts from borrows started by `&mut` arguments. -macro_rules! reverse_decode { - ($reader:ident, $s:ident;) => {}; - ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { - reverse_decode!($reader, $s; $($rest: $rest_ty),*); - let $first = <$first_ty>::decode(&mut $reader, $s); - } -} - -#[allow(unsafe_code)] -mod buffer; -#[forbid(unsafe_code)] -pub mod client; -#[allow(unsafe_code)] -mod closure; -#[forbid(unsafe_code)] -mod handle; -#[macro_use] -#[forbid(unsafe_code)] -mod rpc; -#[allow(unsafe_code)] -mod scoped_cell; -#[allow(unsafe_code)] -mod selfless_reify; -#[forbid(unsafe_code)] -pub mod server; - -use buffer::Buffer; -pub use rpc::PanicMessage; -use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; - -/// Configuration for establishing an active connection between a server and a -/// client. The server creates the bridge config (`run_server` in `server.rs`), -/// then passes it to the client through the function pointer in the `run` field -/// of `client::Client`. The client constructs a local `Bridge` from the config -/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). -#[repr(C)] -pub struct BridgeConfig<'a> { - /// Buffer used to pass initial input to the client. - input: Buffer, - - /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, - - /// If 'true', always invoke the default panic hook - force_show_panics: bool, - - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. - _marker: marker::PhantomData<*mut ()>, -} - -#[forbid(unsafe_code)] -#[allow(non_camel_case_types)] -mod api_tags { - use super::rpc::{DecodeMut, Encode, Reader, Writer}; - - macro_rules! declare_tags { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $( - pub(super) enum $name { - $($method),* - } - rpc_encode_decode!(enum $name { $($method),* }); - )* - - pub(super) enum Method { - $($name($name)),* - } - rpc_encode_decode!(enum Method { $($name(m)),* }); - } - } - with_api!(self, self, declare_tags); -} - -/// Helper to wrap associated types to allow trait impl dispatch. -/// That is, normally a pair of impls for `T::Foo` and `T::Bar` -/// can overlap, but if the impls are, instead, on types like -/// `Marked` and `Marked`, they can't. -trait Mark { - type Unmarked; - fn mark(unmarked: Self::Unmarked) -> Self; -} - -/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). -trait Unmark { - type Unmarked; - fn unmark(self) -> Self::Unmarked; -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct Marked { - value: T, - _marker: marker::PhantomData, -} - -impl Mark for Marked { - type Unmarked = T; - fn mark(unmarked: Self::Unmarked) -> Self { - Marked { value: unmarked, _marker: marker::PhantomData } - } -} -impl Unmark for Marked { - type Unmarked = T; - fn unmark(self) -> Self::Unmarked { - self.value - } -} -impl<'a, T, M> Unmark for &'a Marked { - type Unmarked = &'a T; - fn unmark(self) -> Self::Unmarked { - &self.value - } -} -impl<'a, T, M> Unmark for &'a mut Marked { - type Unmarked = &'a mut T; - fn unmark(self) -> Self::Unmarked { - &mut self.value - } -} - -impl Mark for Vec { - type Unmarked = Vec; - fn mark(unmarked: Self::Unmarked) -> Self { - // Should be a no-op due to std's in-place collect optimizations. - unmarked.into_iter().map(T::mark).collect() - } -} -impl Unmark for Vec { - type Unmarked = Vec; - fn unmark(self) -> Self::Unmarked { - // Should be a no-op due to std's in-place collect optimizations. - self.into_iter().map(T::unmark).collect() - } -} - -macro_rules! mark_noop { - ($($ty:ty),* $(,)?) => { - $( - impl Mark for $ty { - type Unmarked = Self; - fn mark(unmarked: Self::Unmarked) -> Self { - unmarked - } - } - impl Unmark for $ty { - type Unmarked = Self; - fn unmark(self) -> Self::Unmarked { - self - } - } - )* - } -} -mark_noop! { - (), - bool, - char, - &'_ [u8], - &'_ str, - String, - u8, - usize, - Delimiter, - Level, - LineColumn, - Spacing, -} - -rpc_encode_decode!( - enum Delimiter { - Parenthesis, - Brace, - Bracket, - None, - } -); -rpc_encode_decode!( - enum Level { - Error, - Warning, - Note, - Help, - } -); -rpc_encode_decode!(struct LineColumn { line, column }); -rpc_encode_decode!( - enum Spacing { - Alone, - Joint, - } -); - -macro_rules! mark_compound { - (struct $name:ident <$($T:ident),+> { $($field:ident),* $(,)? }) => { - impl<$($T: Mark),+> Mark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn mark(unmarked: Self::Unmarked) -> Self { - $name { - $($field: Mark::mark(unmarked.$field)),* - } - } - } - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn unmark(self) -> Self::Unmarked { - $name { - $($field: Unmark::unmark(self.$field)),* - } - } - } - }; - (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => { - impl<$($T: Mark),+> Mark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn mark(unmarked: Self::Unmarked) -> Self { - match unmarked { - $($name::$variant $(($field))? => { - $name::$variant $((Mark::mark($field)))? - })* - } - } - } - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn unmark(self) -> Self::Unmarked { - match self { - $($name::$variant $(($field))? => { - $name::$variant $((Unmark::unmark($field)))? - })* - } - } - } - } -} - -macro_rules! compound_traits { - ($($t:tt)*) => { - rpc_encode_decode!($($t)*); - mark_compound!($($t)*); - }; -} - -compound_traits!( - enum Bound { - Included(x), - Excluded(x), - Unbounded, - } -); - -compound_traits!( - enum Option { - Some(t), - None, - } -); - -compound_traits!( - enum Result { - Ok(t), - Err(e), - } -); - -#[derive(Copy, Clone)] -pub struct DelimSpan { - pub open: Span, - pub close: Span, - pub entire: Span, -} - -impl DelimSpan { - pub fn from_single(span: Span) -> Self { - DelimSpan { open: span, close: span, entire: span } - } -} - -compound_traits!(struct DelimSpan { open, close, entire }); - -#[derive(Clone)] -pub struct Group { - pub delimiter: Delimiter, - pub stream: Option, - pub span: DelimSpan, -} - -compound_traits!(struct Group { delimiter, stream, span }); - -#[derive(Clone)] -pub struct Punct { - pub ch: u8, - pub joint: bool, - pub span: Span, -} - -compound_traits!(struct Punct { ch, joint, span }); - -#[derive(Clone)] -pub enum TokenTree { - Group(Group), - Punct(Punct), - Ident(Ident), - Literal(Literal), -} - -compound_traits!( - enum TokenTree { - Group(tt), - Punct(tt), - Ident(tt), - Literal(tt), - } -); - -/// Globals provided alongside the initial inputs for a macro expansion. -/// Provides values such as spans which are used frequently to avoid RPC. -#[derive(Clone)] -pub struct ExpnGlobals { - pub def_site: Span, - pub call_site: Span, - pub mixed_site: Span, -} - -compound_traits!( - struct ExpnGlobals { def_site, call_site, mixed_site } -); diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/rpc.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/rpc.rs deleted file mode 100644 index e9d7a46c06f6..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/rpc.rs +++ /dev/null @@ -1,304 +0,0 @@ -//! Serialization for client-server communication. - -use std::any::Any; -use std::char; -use std::io::Write; -use std::num::NonZeroU32; -use std::str; - -pub(super) type Writer = super::buffer::Buffer; - -pub(super) trait Encode: Sized { - fn encode(self, w: &mut Writer, s: &mut S); -} - -pub(super) type Reader<'a> = &'a [u8]; - -pub(super) trait Decode<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s S) -> Self; -} - -pub(super) trait DecodeMut<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; -} - -macro_rules! rpc_encode_decode { - (le $ty:ty) => { - impl Encode for $ty { - fn encode(self, w: &mut Writer, _: &mut S) { - w.extend_from_array(&self.to_le_bytes()); - } - } - - impl DecodeMut<'_, '_, S> for $ty { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { - const N: usize = ::std::mem::size_of::<$ty>(); - - let mut bytes = [0; N]; - bytes.copy_from_slice(&r[..N]); - *r = &r[N..]; - - Self::from_le_bytes(bytes) - } - } - }; - (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { - $(self.$field.encode(w, s);)* - } - } - - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - $name { - $($field: DecodeMut::decode(r, s)),* - } - } - } - }; - (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub const $variant: u8 = Tag::$variant as u8;)* - } - - match self { - $($name::$variant $(($field))* => { - tag::$variant.encode(w, s); - $($field.encode(w, s);)* - })* - } - } - } - - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub const $variant: u8 = Tag::$variant as u8;)* - } - - match u8::decode(r, s) { - $(tag::$variant => { - $(let $field = DecodeMut::decode(r, s);)* - $name::$variant $(($field))* - })* - _ => unreachable!(), - } - } - } - } -} - -impl Encode for () { - fn encode(self, _: &mut Writer, _: &mut S) {} -} - -impl DecodeMut<'_, '_, S> for () { - fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} -} - -impl Encode for u8 { - fn encode(self, w: &mut Writer, _: &mut S) { - w.push(self); - } -} - -impl DecodeMut<'_, '_, S> for u8 { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { - let x = r[0]; - *r = &r[1..]; - x - } -} - -rpc_encode_decode!(le u32); -rpc_encode_decode!(le usize); - -impl Encode for bool { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u8).encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for bool { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - match u8::decode(r, s) { - 0 => false, - 1 => true, - _ => unreachable!(), - } - } -} - -impl Encode for char { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u32).encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for char { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - char::from_u32(u32::decode(r, s)).unwrap() - } -} - -impl Encode for NonZeroU32 { - fn encode(self, w: &mut Writer, s: &mut S) { - self.get().encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for NonZeroU32 { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - Self::new(u32::decode(r, s)).unwrap() - } -} - -impl, B: Encode> Encode for (A, B) { - fn encode(self, w: &mut Writer, s: &mut S) { - self.0.encode(w, s); - self.1.encode(w, s); - } -} - -impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> - for (A, B) -{ - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - (DecodeMut::decode(r, s), DecodeMut::decode(r, s)) - } -} - -impl Encode for &[u8] { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - w.write_all(self).unwrap(); - } -} - -impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - let len = usize::decode(r, s); - let xs = &r[..len]; - *r = &r[len..]; - xs - } -} - -impl Encode for &str { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_bytes().encode(w, s); - } -} - -impl<'a, S> DecodeMut<'a, '_, S> for &'a str { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - str::from_utf8(<&[u8]>::decode(r, s)).unwrap() - } -} - -impl Encode for String { - fn encode(self, w: &mut Writer, s: &mut S) { - self[..].encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for String { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - <&str>::decode(r, s).to_string() - } -} - -impl> Encode for Vec { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - for x in self { - x.encode(w, s); - } - } -} - -impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - let len = usize::decode(r, s); - let mut vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(T::decode(r, s)); - } - vec - } -} - -/// Simplified version of panic payloads, ignoring -/// types other than `&'static str` and `String`. -pub enum PanicMessage { - StaticStr(&'static str), - String(String), - Unknown, -} - -impl From> for PanicMessage { - fn from(payload: Box) -> Self { - if let Some(s) = payload.downcast_ref::<&'static str>() { - return PanicMessage::StaticStr(s); - } - if let Ok(s) = payload.downcast::() { - return PanicMessage::String(*s); - } - PanicMessage::Unknown - } -} - -impl Into> for PanicMessage { - fn into(self) -> Box { - match self { - PanicMessage::StaticStr(s) => Box::new(s), - PanicMessage::String(s) => Box::new(s), - PanicMessage::Unknown => { - struct UnknownPanicMessage; - Box::new(UnknownPanicMessage) - } - } - } -} - -impl PanicMessage { - pub fn as_str(&self) -> Option<&str> { - match self { - PanicMessage::StaticStr(s) => Some(s), - PanicMessage::String(s) => Some(s), - PanicMessage::Unknown => None, - } - } -} - -impl Encode for PanicMessage { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_str().encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for PanicMessage { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - match Option::::decode(r, s) { - Some(s) => PanicMessage::String(s), - None => PanicMessage::Unknown, - } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/scoped_cell.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/scoped_cell.rs deleted file mode 100644 index 2cde1f65adf9..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/scoped_cell.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! `Cell` variant for (scoped) existential lifetimes. - -use std::cell::Cell; -use std::mem; -use std::ops::{Deref, DerefMut}; - -/// Type lambda application, with a lifetime. -#[allow(unused_lifetimes)] -pub trait ApplyL<'a> { - type Out; -} - -/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. -pub trait LambdaL: for<'a> ApplyL<'a> {} - -impl ApplyL<'a>> LambdaL for T {} - -// HACK(eddyb) work around projection limitations with a newtype -// FIXME(#52812) replace with `&'a mut >::Out` -pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut >::Out); - -impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { - type Target = >::Out; - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -pub struct ScopedCell(Cell<>::Out>); - -impl ScopedCell { - pub const fn new(value: >::Out) -> Self { - ScopedCell(Cell::new(value)) - } - - /// Sets the value in `self` to `replacement` while - /// running `f`, which gets the old value, mutably. - /// The old value will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - pub fn replace<'a, R>( - &self, - replacement: >::Out, - f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, - ) -> R { - /// Wrapper that ensures that the cell always gets filled - /// (with the original state, optionally changed by `f`), - /// even if `f` had panicked. - struct PutBackOnDrop<'a, T: LambdaL> { - cell: &'a ScopedCell, - value: Option<>::Out>, - } - - impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { - fn drop(&mut self) { - self.cell.0.set(self.value.take().unwrap()); - } - } - - let mut put_back_on_drop = PutBackOnDrop { - cell: self, - value: Some(self.0.replace(unsafe { - let erased = mem::transmute_copy(&replacement); - mem::forget(replacement); - erased - })), - }; - - f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) - } - - /// Sets the value in `self` to `value` while running `f`. - pub fn set(&self, value: >::Out, f: impl FnOnce() -> R) -> R { - self.replace(value, |_| f()) - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/selfless_reify.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/selfless_reify.rs deleted file mode 100644 index 907ad256e4b4..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/selfless_reify.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Abstraction for creating `fn` pointers from any callable that *effectively* -//! has the equivalent of implementing `Default`, even if the compiler neither -//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers) -//! other than those with absolutely no captures. -//! -//! More specifically, for a closure-like type to be "effectively `Default`": -//! * it must be a ZST (zero-sized type): no information contained within, so -//! that `Default`'s return value (if it were implemented) is unambiguous -//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar -//! types that would make duplicating values at will unsound -//! * combined with the ZST requirement, this confers a kind of "telecopy" -//! ability: similar to `Copy`, but without keeping the value around, and -//! instead "reconstructing" it (a noop given it's a ZST) when needed -//! * it must be *provably* inhabited: no captured uninhabited types or any -//! other types that cannot be constructed by the user of this abstraction -//! * the proof is a value of the closure-like type itself, in a sense the -//! "seed" for the "telecopy" process made possible by ZST + `Copy` -//! * this requirement is the only reason an abstraction limited to a specific -//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic -//! at the "attempted `::default()` call" time, but that doesn't guarantee -//! that the value can be soundly created, and attempting to use the typical -//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type -//! that is not proof of anything without a value (i.e. isomorphic to a -//! newtype of the type it's trying to prove the inhabitation of) -//! -//! A more flexible (and safer) solution to the general problem could exist once -//! `const`-generic parameters can have type parameters in their types: -//! -//! ```rust,ignore (needs future const-generics) -//! extern "C" fn ffi_wrapper< -//! A, R, -//! F: Fn(A) -> R, -//! const f: F, // <-- this `const`-generic is not yet allowed -//! >(arg: A) -> R { -//! f(arg) -//! } -//! ``` - -use std::mem; - -// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement. -macro_rules! define_reify_functions { - ($( - fn $name:ident $(<$($param:ident),*>)? - for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; - )+) => { - $(pub const fn $name< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { - // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic - // formatting becomes possible in `const fn`. - assert!(mem::size_of::() == 0, "selfless_reify: closure must be zero-sized"); - - $(extern $abi)? fn wrapper< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >($($arg: $arg_ty),*) -> $ret_ty { - let f = unsafe { - // SAFETY: `F` satisfies all criteria for "out of thin air" - // reconstructability (see module-level doc comment). - mem::MaybeUninit::::uninit().assume_init() - }; - f($($arg),*) - } - let _f_proof = f; - wrapper::< - $($($param,)*)? - F - > - })+ - } -} - -define_reify_functions! { - fn _reify_to_extern_c_fn_unary for extern "C" fn(arg: A) -> R; - - // HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>) - // -> T` but that doesn't work with just `reify_to_extern_c_fn_unary` - // because of the `fn` pointer type being "higher-ranked" (i.e. the - // `for<'a>` binder). - // FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R; -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/server.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/server.rs deleted file mode 100644 index 6e7a8d8c10df..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/bridge/server.rs +++ /dev/null @@ -1,339 +0,0 @@ -//! Server-side traits. - -use super::*; - -// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. -use super::client::HandleStore; - -pub trait Types { - type FreeFunctions: 'static; - type TokenStream: 'static + Clone; - type Ident: 'static + Copy + Eq + Hash; - type Literal: 'static + Clone; - type SourceFile: 'static + Clone; - type MultiSpan: 'static; - type Diagnostic: 'static; - type Span: 'static + Copy + Eq + Hash; -} - -/// Declare an associated fn of one of the traits below, adding necessary -/// default bodies. -macro_rules! associated_fn { - (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => - (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); - - (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => - (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); - - ($($item:tt)*) => ($($item)*;) -} - -macro_rules! declare_server_traits { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(pub trait $name: Types { - $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* - })* - - pub trait Server: Types $(+ $name)* { - fn globals(&mut self) -> ExpnGlobals; - } - } -} -with_api!(Self, self_, declare_server_traits); - -pub(super) struct MarkedTypes(S); - -impl Server for MarkedTypes { - fn globals(&mut self) -> ExpnGlobals { - <_>::mark(Server::globals(&mut self.0)) - } -} - -macro_rules! define_mark_types_impls { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - impl Types for MarkedTypes { - $(type $name = Marked;)* - } - - $(impl $name for MarkedTypes { - $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) - })* - })* - } -} -with_api!(Self, self_, define_mark_types_impls); - -struct Dispatcher { - handle_store: HandleStore, - server: S, -} - -macro_rules! define_dispatcher_impl { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. - pub trait DispatcherTrait { - // HACK(eddyb) these are here to allow `Self::$name` to work below. - $(type $name;)* - fn dispatch(&mut self, buf: Buffer) -> Buffer; - } - - impl DispatcherTrait for Dispatcher> { - $(type $name = as Types>::$name;)* - fn dispatch(&mut self, mut buf: Buffer) -> Buffer { - let Dispatcher { handle_store, server } = self; - - let mut reader = &buf[..]; - match api_tags::Method::decode(&mut reader, &mut ()) { - $(api_tags::Method::$name(m) => match m { - $(api_tags::$name::$method => { - let mut call_method = || { - reverse_decode!(reader, handle_store; $($arg: $arg_ty),*); - $name::$method(server, $($arg),*) - }; - // HACK(eddyb) don't use `panic::catch_unwind` in a panic. - // If client and server happen to use the same `libstd`, - // `catch_unwind` asserts that the panic counter was 0, - // even when the closure passed to it didn't panic. - let r = if thread::panicking() { - Ok(call_method()) - } else { - panic::catch_unwind(panic::AssertUnwindSafe(call_method)) - .map_err(PanicMessage::from) - }; - - buf.clear(); - r.encode(&mut buf, handle_store); - })* - }),* - } - buf - } - } - } -} -with_api!(Self, self_, define_dispatcher_impl); - -pub trait ExecutionStrategy { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer; -} - -pub struct SameThread; - -impl ExecutionStrategy for SameThread { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - let mut dispatch = |buf| dispatcher.dispatch(buf); - - run_client(BridgeConfig { - input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) - } -} - -// NOTE(eddyb) Two implementations are provided, the second one is a bit -// faster but neither is anywhere near as fast as same-thread execution. - -pub struct CrossThread1; - -impl ExecutionStrategy for CrossThread1 { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - use std::sync::mpsc::channel; - - let (req_tx, req_rx) = channel(); - let (res_tx, res_rx) = channel(); - - let join_handle = thread::spawn(move || { - let mut dispatch = |buf| { - req_tx.send(buf).unwrap(); - res_rx.recv().unwrap() - }; - - run_client(BridgeConfig { - input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) - }); - - for b in req_rx { - res_tx.send(dispatcher.dispatch(b)).unwrap(); - } - - join_handle.join().unwrap() - } -} - -pub struct CrossThread2; - -impl ExecutionStrategy for CrossThread2 { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - use std::sync::{Arc, Mutex}; - - enum State { - Req(T), - Res(T), - } - - let mut state = Arc::new(Mutex::new(State::Res(Buffer::new()))); - - let server_thread = thread::current(); - let state2 = state.clone(); - let join_handle = thread::spawn(move || { - let mut dispatch = |b| { - *state2.lock().unwrap() = State::Req(b); - server_thread.unpark(); - loop { - thread::park(); - if let State::Res(b) = &mut *state2.lock().unwrap() { - break b.take(); - } - } - }; - - let r = run_client(BridgeConfig { - input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }); - - // Wake up the server so it can exit the dispatch loop. - drop(state2); - server_thread.unpark(); - - r - }); - - // Check whether `state2` was dropped, to know when to stop. - while Arc::get_mut(&mut state).is_none() { - thread::park(); - let mut b = match &mut *state.lock().unwrap() { - State::Req(b) => b.take(), - _ => continue, - }; - b = dispatcher.dispatch(b.take()); - *state.lock().unwrap() = State::Res(b); - join_handle.thread().unpark(); - } - - join_handle.join().unwrap() - } -} - -fn run_server< - S: Server, - I: Encode>>, - O: for<'a, 's> DecodeMut<'a, 's, HandleStore>>, ->( - strategy: &impl ExecutionStrategy, - handle_counters: &'static client::HandleCounters, - server: S, - input: I, - run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer, - force_show_panics: bool, -) -> Result { - let mut dispatcher = - Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; - - let globals = dispatcher.server.globals(); - - let mut buf = Buffer::new(); - (globals, input).encode(&mut buf, &mut dispatcher.handle_store); - - buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); - - Result::decode(&mut &buf[..], &mut dispatcher.handle_store) -} - -impl client::Client { - pub fn run( - &self, - strategy: &impl ExecutionStrategy, - server: S, - input: S::TokenStream, - force_show_panics: bool, - ) -> Result - where - S: Server, - S::TokenStream: Default, - { - let client::Client { get_handle_counters, run, _marker } = *self; - run_server( - strategy, - get_handle_counters(), - server, - as Types>::TokenStream::mark(input), - run, - force_show_panics, - ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) - } -} - -impl - client::Client< - (super::super::TokenStream, super::super::TokenStream), - super::super::TokenStream, - > -{ - pub fn run( - &self, - strategy: &impl ExecutionStrategy, - server: S, - input: S::TokenStream, - input2: S::TokenStream, - force_show_panics: bool, - ) -> Result - where - S: Server, - S::TokenStream: Default, - { - let client::Client { get_handle_counters, run, _marker } = *self; - run_server( - strategy, - get_handle_counters(), - server, - ( - as Types>::TokenStream::mark(input), - as Types>::TokenStream::mark(input2), - ), - run, - force_show_panics, - ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/diagnostic.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/diagnostic.rs deleted file mode 100644 index 3fade2dc4f9c..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/diagnostic.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! lib-proc-macro diagnostic -//! -//! Copy from -//! augmented with removing unstable features - -use super::Span; - -/// An enum representing a diagnostic level. -#[derive(Copy, Clone, Debug)] -#[non_exhaustive] -pub enum Level { - /// An error. - Error, - /// A warning. - Warning, - /// A note. - Note, - /// A help message. - Help, -} - -/// Trait implemented by types that can be converted into a set of `Span`s. -pub trait MultiSpan { - /// Converts `self` into a `Vec`. - fn into_spans(self) -> Vec; -} - -impl MultiSpan for Span { - fn into_spans(self) -> Vec { - vec![self] - } -} - -impl MultiSpan for Vec { - fn into_spans(self) -> Vec { - self - } -} - -impl<'a> MultiSpan for &'a [Span] { - fn into_spans(self) -> Vec { - self.to_vec() - } -} - -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec, - children: Vec, -} - -macro_rules! diagnostic_child_methods { - ($spanned:ident, $regular:ident, $level:expr) => { - #[doc = concat!("Adds a new child diagnostics message to `self` with the [`", - stringify!($level), "`] level, and the given `spans` and `message`.")] - pub fn $spanned(mut self, spans: S, message: T) -> Diagnostic - where - S: MultiSpan, - T: Into, - { - self.children.push(Diagnostic::spanned(spans, $level, message)); - self - } - - #[doc = concat!("Adds a new child diagnostic message to `self` with the [`", - stringify!($level), "`] level, and the given `message`.")] - pub fn $regular>(mut self, message: T) -> Diagnostic { - self.children.push(Diagnostic::new($level, message)); - self - } - }; -} - -/// Iterator over the children diagnostics of a `Diagnostic`. -#[derive(Debug, Clone)] -pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>); - -impl<'a> Iterator for Children<'a> { - type Item = &'a Diagnostic; - - fn next(&mut self) -> Option { - self.0.next() - } -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } - - /// Creates a new diagnostic with the given `level` and `message` pointing to - /// the given set of `spans`. - pub fn spanned(spans: S, level: Level, message: T) -> Diagnostic - where - S: MultiSpan, - T: Into, - { - Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] } - } - - diagnostic_child_methods!(span_error, error, Level::Error); - diagnostic_child_methods!(span_warning, warning, Level::Warning); - diagnostic_child_methods!(span_note, note, Level::Note); - diagnostic_child_methods!(span_help, help, Level::Help); - - /// Returns the diagnostic `level` for `self`. - pub fn level(&self) -> Level { - self.level - } - - /// Sets the level in `self` to `level`. - pub fn set_level(&mut self, level: Level) { - self.level = level; - } - - /// Returns the message in `self`. - pub fn message(&self) -> &str { - &self.message - } - - /// Sets the message in `self` to `message`. - pub fn set_message>(&mut self, message: T) { - self.message = message.into(); - } - - /// Returns the `Span`s in `self`. - pub fn spans(&self) -> &[Span] { - &self.spans - } - - /// Sets the `Span`s in `self` to `spans`. - pub fn set_spans(&mut self, spans: S) { - self.spans = spans.into_spans(); - } - - /// Returns an iterator over the children diagnostics of `self`. - pub fn children(&self) -> Children<'_> { - Children(self.children.iter()) - } - - /// Emit the diagnostic. - pub fn emit(self) { - fn to_internal(spans: Vec) -> super::bridge::client::MultiSpan { - let mut multi_span = super::bridge::client::MultiSpan::new(); - for span in spans { - multi_span.push(span.0); - } - multi_span - } - - let mut diag = super::bridge::client::Diagnostic::new( - self.level, - &self.message[..], - to_internal(self.spans), - ); - for c in self.children { - diag.sub(c.level, &c.message[..], to_internal(c.spans)); - } - diag.emit(); - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/mod.rs deleted file mode 100644 index be62c73ef32b..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/mod.rs +++ /dev/null @@ -1,1125 +0,0 @@ -//! A support library for macro authors when defining new macros. -//! -//! This library, provided by the standard distribution, provides the types -//! consumed in the interfaces of procedurally defined macro definitions such as -//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and -//! custom derive attributes`#[proc_macro_derive]`. -//! -//! See [the book] for more. -//! -//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes - -#[doc(hidden)] -pub mod bridge; - -mod diagnostic; - -pub use diagnostic::{Diagnostic, Level, MultiSpan}; - -use std::cmp::Ordering; -use std::ops::RangeBounds; -use std::path::PathBuf; -use std::str::FromStr; -use std::{error, fmt, iter, mem}; - -/// Determines whether proc_macro has been made accessible to the currently -/// running program. -/// -/// The proc_macro crate is only intended for use inside the implementation of -/// procedural macros. All the functions in this crate panic if invoked from -/// outside of a procedural macro, such as from a build script or unit test or -/// ordinary Rust binary. -/// -/// With consideration for Rust libraries that are designed to support both -/// macro and non-macro use cases, `proc_macro::is_available()` provides a -/// non-panicking way to detect whether the infrastructure required to use the -/// API of proc_macro is presently available. Returns true if invoked from -/// inside of a procedural macro, false if invoked from any other binary. -pub fn is_available() -> bool { - bridge::client::is_available() -} - -/// The main type provided by this crate, representing an abstract stream of -/// tokens, or, more specifically, a sequence of token trees. -/// The type provide interfaces for iterating over those token trees and, conversely, -/// collecting a number of token trees into one stream. -/// -/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` -/// and `#[proc_macro_derive]` definitions. -#[derive(Clone)] -pub struct TokenStream(Option); - -/// Error returned from `TokenStream::from_str`. -#[non_exhaustive] -#[derive(Debug)] -pub struct LexError; - -impl fmt::Display for LexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("cannot parse string into token stream") - } -} - -impl error::Error for LexError {} - -/// Error returned from `TokenStream::expand_expr`. -#[non_exhaustive] -#[derive(Debug)] -pub struct ExpandError; - -impl fmt::Display for ExpandError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("macro expansion failed") - } -} - -impl error::Error for ExpandError {} - -impl TokenStream { - /// Returns an empty `TokenStream` containing no token trees. - pub fn new() -> TokenStream { - TokenStream(None) - } - - /// Checks if this `TokenStream` is empty. - pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) - } - - /// Parses this `TokenStream` as an expression and attempts to expand any - /// macros within it. Returns the expanded `TokenStream`. - /// - /// Currently only expressions expanding to literals will succeed, although - /// this may be relaxed in the future. - /// - /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded, - /// report an error, failing compilation, and/or return an `Err(..)`. The - /// specific behavior for any error condition, and what conditions are - /// considered errors, is unspecified and may change in the future. - pub fn expand_expr(&self) -> Result { - let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::TokenStream::expand_expr(stream) { - Ok(stream) => Ok(TokenStream(Some(stream))), - Err(_) => Err(ExpandError), - } - } -} - -/// Attempts to break the string into tokens and parse those tokens into a token stream. -/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters -/// or characters not existing in the language. -/// All tokens in the parsed stream get `Span::call_site()` spans. -/// -/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to -/// change these errors into `LexError`s later. -impl FromStr for TokenStream { - type Err = LexError; - - fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) - } -} - -/// Prints the token stream as a string that is supposed to be losslessly convertible back -/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -impl fmt::Display for TokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -/// Prints token in a form convenient for debugging. -impl fmt::Debug for TokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("TokenStream ")?; - f.debug_list().entries(self.clone()).finish() - } -} - -impl Default for TokenStream { - fn default() -> Self { - TokenStream::new() - } -} - -pub use quote::{quote, quote_span}; - -fn tree_to_bridge_tree( - tree: TokenTree, -) -> bridge::TokenTree< - bridge::client::TokenStream, - bridge::client::Span, - bridge::client::Ident, - bridge::client::Literal, -> { - match tree { - TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), - TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), - TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), - TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), - } -} - -/// Creates a token stream containing a single token tree. -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) - } -} - -/// Non-generic helper for implementing `FromIterator` and -/// `Extend` with less monomorphization in calling crates. -struct ConcatStreamsHelper { - streams: Vec, -} - -impl ConcatStreamsHelper { - fn new(capacity: usize) -> Self { - ConcatStreamsHelper { streams: Vec::with_capacity(capacity) } - } - - fn push(&mut self, stream: TokenStream) { - if let Some(stream) = stream.0 { - self.streams.push(stream); - } - } - - fn build(mut self) -> TokenStream { - if self.streams.len() <= 1 { - TokenStream(self.streams.pop()) - } else { - TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) - } - } - - fn append_to(mut self, stream: &mut TokenStream) { - if self.streams.is_empty() { - return; - } - let base = stream.0.take(); - if base.is_none() && self.streams.len() == 1 { - stream.0 = self.streams.pop(); - } else { - stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); - } - } -} - -/// Collects a number of token trees into a single stream. -impl iter::FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl iter::FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { - let iter = streams.into_iter(); - let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); - iter.for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { - // FIXME(eddyb) Use an optimized implementation if/when possible. - *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect(); - } -} - -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { - use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - #[derive(Clone)] - pub struct IntoIter( - std::vec::IntoIter< - bridge::TokenTree< - bridge::client::TokenStream, - bridge::client::Span, - bridge::client::Ident, - bridge::client::Literal, - >, - >, - ); - - impl Iterator for IntoIter { - type Item = TokenTree; - - fn next(&mut self) -> Option { - self.0.next().map(|tree| match tree { - bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), - bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), - bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), - bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), - }) - } - } - - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) - } - } -} - -#[doc(hidden)] -mod quote; - -/// A region of source code, along with macro expansion information. -#[derive(Copy, Clone)] -pub struct Span(bridge::client::Span); - -macro_rules! diagnostic_method { - ($name:ident, $level:expr) => { - /// Creates a new `Diagnostic` with the given `message` at the span - /// `self`. - pub fn $name>(self, message: T) -> Diagnostic { - Diagnostic::spanned(self, $level, message) - } - }; -} - -impl Span { - /// A span that resolves at the macro definition site. - pub fn def_site() -> Span { - Span(bridge::client::Span::def_site()) - } - - /// The span of the invocation of the current procedural macro. - /// Identifiers created with this span will be resolved as if they were written - /// directly at the macro call location (call-site hygiene) and other code - /// at the macro call site will be able to refer to them as well. - pub fn call_site() -> Span { - Span(bridge::client::Span::call_site()) - } - - /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro - /// definition site (local variables, labels, `$crate`) and sometimes at the macro - /// call site (everything else). - /// The span location is taken from the call-site. - pub fn mixed_site() -> Span { - Span(bridge::client::Span::mixed_site()) - } - - /// The original source file into which this span points. - pub fn source_file(&self) -> SourceFile { - SourceFile(self.0.source_file()) - } - - /// The `Span` for the tokens in the previous macro expansion from which - /// `self` was generated from, if any. - pub fn parent(&self) -> Option { - self.0.parent().map(Span) - } - - /// The span for the origin source code that `self` was generated from. If - /// this `Span` wasn't generated from other macro expansions then the return - /// value is the same as `*self`. - pub fn source(&self) -> Span { - Span(self.0.source()) - } - - /// Gets the starting line/column in the source file for this span. - pub fn start(&self) -> LineColumn { - self.0.start().add_1_to_column() - } - - /// Gets the ending line/column in the source file for this span. - pub fn end(&self) -> LineColumn { - self.0.end().add_1_to_column() - } - - /// Creates an empty span pointing to directly before this span. - pub fn before(&self) -> Span { - Span(self.0.before()) - } - - /// Creates an empty span pointing to directly after this span. - pub fn after(&self) -> Span { - Span(self.0.after()) - } - - /// Creates a new span encompassing `self` and `other`. - /// - /// Returns `None` if `self` and `other` are from different files. - pub fn join(&self, other: Span) -> Option { - self.0.join(other.0).map(Span) - } - - /// Creates a new span with the same line/column information as `self` but - /// that resolves symbols as though it were at `other`. - pub fn resolved_at(&self, other: Span) -> Span { - Span(self.0.resolved_at(other.0)) - } - - /// Creates a new span with the same name resolution behavior as `self` but - /// with the line/column information of `other`. - pub fn located_at(&self, other: Span) -> Span { - other.resolved_at(*self) - } - - /// Compares to spans to see if they're equal. - pub fn eq(&self, other: &Span) -> bool { - self.0 == other.0 - } - - /// Returns the source text behind a span. This preserves the original source - /// code, including spaces and comments. It only returns a result if the span - /// corresponds to real source code. - /// - /// Note: The observable result of a macro should only rely on the tokens and - /// not on this source text. The result of this function is a best effort to - /// be used for diagnostics only. - pub fn source_text(&self) -> Option { - self.0.source_text() - } - - // Used by the implementation of `Span::quote` - #[doc(hidden)] - pub fn save_span(&self) -> usize { - self.0.save_span() - } - - // Used by the implementation of `Span::quote` - #[doc(hidden)] - pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::Span::recover_proc_macro_span(id)) - } - - diagnostic_method!(error, Level::Error); - diagnostic_method!(warning, Level::Warning); - diagnostic_method!(note, Level::Note); - diagnostic_method!(help, Level::Help); -} - -/// Prints a span in a form convenient for debugging. -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// A line-column pair representing the start or end of a `Span`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LineColumn { - /// The 1-indexed line in the source file on which the span starts or ends (inclusive). - pub line: usize, - /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source - /// file on which the span starts or ends (inclusive). - pub column: usize, -} - -impl LineColumn { - fn add_1_to_column(self) -> Self { - LineColumn { line: self.line, column: self.column + 1 } - } -} - -impl Ord for LineColumn { - fn cmp(&self, other: &Self) -> Ordering { - self.line.cmp(&other.line).then(self.column.cmp(&other.column)) - } -} - -impl PartialOrd for LineColumn { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// The source file of a given `Span`. -#[derive(Clone)] -pub struct SourceFile(bridge::client::SourceFile); - -impl SourceFile { - /// Gets the path to this source file. - /// - /// ### Note - /// If the code span associated with this `SourceFile` was generated by an external macro, this - /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check. - /// - /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on - /// the command line, the path as given might not actually be valid. - /// - /// [`is_real`]: Self::is_real - pub fn path(&self) -> PathBuf { - PathBuf::from(self.0.path()) - } - - /// Returns `true` if this source file is a real source file, and not generated by an external - /// macro's expansion. - pub fn is_real(&self) -> bool { - // This is a hack until intercrate spans are implemented and we can have real source files - // for spans generated in external macros. - // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 - self.0.is_real() - } -} - -impl fmt::Debug for SourceFile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SourceFile") - .field("path", &self.path()) - .field("is_real", &self.is_real()) - .finish() - } -} - -impl PartialEq for SourceFile { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl Eq for SourceFile {} - -/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). -#[derive(Clone)] -pub enum TokenTree { - /// A token stream surrounded by bracket delimiters. - Group(Group), - /// An identifier. - Ident(Ident), - /// A single punctuation character (`+`, `,`, `$`, etc.). - Punct(Punct), - /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. - Literal(Literal), -} - -impl TokenTree { - /// Returns the span of this tree, delegating to the `span` method of - /// the contained token or a delimited stream. - pub fn span(&self) -> Span { - match *self { - TokenTree::Group(ref t) => t.span(), - TokenTree::Ident(ref t) => t.span(), - TokenTree::Punct(ref t) => t.span(), - TokenTree::Literal(ref t) => t.span(), - } - } - - /// Configures the span for *only this token*. - /// - /// Note that if this token is a `Group` then this method will not configure - /// the span of each of the internal tokens, this will simply delegate to - /// the `set_span` method of each variant. - pub fn set_span(&mut self, span: Span) { - match *self { - TokenTree::Group(ref mut t) => t.set_span(span), - TokenTree::Ident(ref mut t) => t.set_span(span), - TokenTree::Punct(ref mut t) => t.set_span(span), - TokenTree::Literal(ref mut t) => t.set_span(span), - } - } -} - -/// Prints token tree in a form convenient for debugging. -impl fmt::Debug for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Each of these has the name in the struct type in the derived debug, - // so don't bother with an extra layer of indirection - match *self { - TokenTree::Group(ref tt) => tt.fmt(f), - TokenTree::Ident(ref tt) => tt.fmt(f), - TokenTree::Punct(ref tt) => tt.fmt(f), - TokenTree::Literal(ref tt) => tt.fmt(f), - } - } -} - -impl From for TokenTree { - fn from(g: Group) -> TokenTree { - TokenTree::Group(g) - } -} - -impl From for TokenTree { - fn from(g: Ident) -> TokenTree { - TokenTree::Ident(g) - } -} - -impl From for TokenTree { - fn from(g: Punct) -> TokenTree { - TokenTree::Punct(g) - } -} - -impl From for TokenTree { - fn from(g: Literal) -> TokenTree { - TokenTree::Literal(g) - } -} - -/// Prints the token tree as a string that is supposed to be losslessly convertible back -/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -impl fmt::Display for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -/// A delimited token stream. -/// -/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. -#[derive(Clone)] -pub struct Group(bridge::Group); - -/// Describes how a sequence of token trees is delimited. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Delimiter { - /// `( ... )` - Parenthesis, - /// `{ ... }` - Brace, - /// `[ ... ]` - Bracket, - /// `Ø ... Ø` - /// An invisible delimiter, that may, for example, appear around tokens coming from a - /// "macro variable" `$var`. It is important to preserve operator priorities in cases like - /// `$var * 3` where `$var` is `1 + 2`. - /// Invisible delimiters might not survive roundtrip of a token stream through a string. - None, -} - -impl Group { - /// Creates a new `Group` with the given delimiter and token stream. - /// - /// This constructor will set the span for this group to - /// `Span::call_site()`. To change the span you can use the `set_span` - /// method below. - pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { - Group(bridge::Group { - delimiter, - stream: stream.0, - span: bridge::DelimSpan::from_single(Span::call_site().0), - }) - } - - /// Returns the delimiter of this `Group` - pub fn delimiter(&self) -> Delimiter { - self.0.delimiter - } - - /// Returns the `TokenStream` of tokens that are delimited in this `Group`. - /// - /// Note that the returned token stream does not include the delimiter - /// returned above. - pub fn stream(&self) -> TokenStream { - TokenStream(self.0.stream.clone()) - } - - /// Returns the span for the delimiters of this token stream, spanning the - /// entire `Group`. - /// - /// ```text - /// pub fn span(&self) -> Span { - /// ^^^^^^^ - /// ``` - pub fn span(&self) -> Span { - Span(self.0.span.entire) - } - - /// Returns the span pointing to the opening delimiter of this group. - /// - /// ```text - /// pub fn span_open(&self) -> Span { - /// ^ - /// ``` - pub fn span_open(&self) -> Span { - Span(self.0.span.open) - } - - /// Returns the span pointing to the closing delimiter of this group. - /// - /// ```text - /// pub fn span_close(&self) -> Span { - /// ^ - /// ``` - pub fn span_close(&self) -> Span { - Span(self.0.span.close) - } - - /// Configures the span for this `Group`'s delimiters, but not its internal - /// tokens. - /// - /// This method will **not** set the span of all the internal tokens spanned - /// by this group, but rather it will only set the span of the delimiter - /// tokens at the level of the `Group`. - pub fn set_span(&mut self, span: Span) { - self.0.span = bridge::DelimSpan::from_single(span.0); - } -} - -/// Prints the group as a string that should be losslessly convertible back -/// into the same group (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters. -impl fmt::Display for Group { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -impl fmt::Debug for Group { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Group") - .field("delimiter", &self.delimiter()) - .field("stream", &self.stream()) - .field("span", &self.span()) - .finish() - } -} - -/// A `Punct` is a single punctuation character such as `+`, `-` or `#`. -/// -/// Multi-character operators like `+=` are represented as two instances of `Punct` with different -/// forms of `Spacing` returned. -#[derive(Clone)] -pub struct Punct(bridge::Punct); - -/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or -/// by a different token or whitespace ([`Spacing::Alone`]). -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Spacing { - /// A `Punct` is not immediately followed by another `Punct`. - /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`. - Alone, - /// A `Punct` is immediately followed by another `Punct`. - /// E.g. `+` is `Joint` in `+=` and `++`. - /// - /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`. - Joint, -} - -impl Punct { - /// Creates a new `Punct` from the given character and spacing. - /// The `ch` argument must be a valid punctuation character permitted by the language, - /// otherwise the function will panic. - /// - /// The returned `Punct` will have the default span of `Span::call_site()` - /// which can be further configured with the `set_span` method below. - pub fn new(ch: char, spacing: Spacing) -> Punct { - const LEGAL_CHARS: &[char] = &[ - '=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', '&', '|', '@', '.', ',', ';', - ':', '#', '$', '?', '\'', - ]; - if !LEGAL_CHARS.contains(&ch) { - panic!("unsupported character `{:?}`", ch); - } - Punct(bridge::Punct { - ch: ch as u8, - joint: spacing == Spacing::Joint, - span: Span::call_site().0, - }) - } - - /// Returns the value of this punctuation character as `char`. - pub fn as_char(&self) -> char { - self.0.ch as char - } - - /// Returns the spacing of this punctuation character, indicating whether it's immediately - /// followed by another `Punct` in the token stream, so they can potentially be combined into - /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace - /// (`Alone`) so the operator has certainly ended. - pub fn spacing(&self) -> Spacing { - if self.0.joint { - Spacing::Joint - } else { - Spacing::Alone - } - } - - /// Returns the span for this punctuation character. - pub fn span(&self) -> Span { - Span(self.0.span) - } - - /// Configure the span for this punctuation character. - pub fn set_span(&mut self, span: Span) { - self.0.span = span.0; - } -} - -/// Prints the punctuation character as a string that should be losslessly convertible -/// back into the same character. -impl fmt::Display for Punct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -impl fmt::Debug for Punct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Punct") - .field("ch", &self.as_char()) - .field("spacing", &self.spacing()) - .field("span", &self.span()) - .finish() - } -} - -impl PartialEq for Punct { - fn eq(&self, rhs: &char) -> bool { - self.as_char() == *rhs - } -} - -impl PartialEq for char { - fn eq(&self, rhs: &Punct) -> bool { - *self == rhs.as_char() - } -} - -/// An identifier (`ident`). -#[derive(Clone)] -pub struct Ident(bridge::client::Ident); - -impl Ident { - /// Creates a new `Ident` with the given `string` as well as the specified - /// `span`. - /// The `string` argument must be a valid identifier permitted by the - /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. - /// - /// Note that `span`, currently in rustc, configures the hygiene information - /// for this identifier. - /// - /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene - /// meaning that identifiers created with this span will be resolved as if they were written - /// directly at the location of the macro call, and other code at the macro call site will be - /// able to refer to them as well. - /// - /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene - /// meaning that identifiers created with this span will be resolved at the location of the - /// macro definition and other code at the macro call site will not be able to refer to them. - /// - /// Due to the current importance of hygiene this constructor, unlike other - /// tokens, requires a `Span` to be specified at construction. - pub fn new(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, false)) - } - - /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). - /// The `string` argument be a valid identifier permitted by the language - /// (including keywords, e.g. `fn`). Keywords which are usable in path segments - /// (e.g. `self`, `super`) are not supported, and will cause a panic. - pub fn new_raw(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, true)) - } - - /// Returns the span of this `Ident`, encompassing the entire string returned - /// by [`to_string`](Self::to_string). - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span of this `Ident`, possibly changing its hygiene context. - pub fn set_span(&mut self, span: Span) { - self.0 = self.0.with_span(span.0); - } -} - -/// Prints the identifier as a string that should be losslessly convertible -/// back into the same identifier. -impl fmt::Display for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Ident") - .field("ident", &self.to_string()) - .field("span", &self.span()) - .finish() - } -} - -/// A literal string (`"hello"`), byte string (`b"hello"`), -/// character (`'a'`), byte character (`b'a'`), an integer or floating point number -/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). -/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. -#[derive(Clone)] -pub struct Literal(bridge::client::Literal); - -macro_rules! suffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new suffixed integer literal with the specified value. - /// - /// This function will create an integer like `1u32` where the integer - /// value specified is the first part of the token and the integral is - /// also suffixed at the end. - /// Literals created from negative numbers might not survive round-trips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind))) - } - )*) -} - -macro_rules! unsuffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new unsuffixed integer literal with the specified value. - /// - /// This function will create an integer like `1` where the integer - /// value specified is the first part of the token. No suffix is - /// specified on this token, meaning that invocations like - /// `Literal::i8_unsuffixed(1)` are equivalent to - /// `Literal::u32_unsuffixed(1)`. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::integer(&n.to_string())) - } - )*) -} - -impl Literal { - suffixed_int_literals! { - u8_suffixed => u8, - u16_suffixed => u16, - u32_suffixed => u32, - u64_suffixed => u64, - u128_suffixed => u128, - usize_suffixed => usize, - i8_suffixed => i8, - i16_suffixed => i16, - i32_suffixed => i32, - i64_suffixed => i64, - i128_suffixed => i128, - isize_suffixed => isize, - } - - unsuffixed_int_literals! { - u8_unsuffixed => u8, - u16_unsuffixed => u16, - u32_unsuffixed => u32, - u64_unsuffixed => u64, - u128_unsuffixed => u128, - usize_unsuffixed => usize, - i8_unsuffixed => i8, - i16_unsuffixed => i16, - i32_unsuffixed => i32, - i64_unsuffixed => i64, - i128_unsuffixed => i128, - isize_unsuffixed => isize, - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f32_unsuffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - let mut repr = n.to_string(); - if !repr.contains('.') { - repr.push_str(".0"); - } - Literal(bridge::client::Literal::float(&repr)) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f32` where the value - /// specified is the preceding part of the token and `f32` is the suffix of - /// the token. This token will always be inferred to be an `f32` in the - /// compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f32_suffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - Literal(bridge::client::Literal::f32(&n.to_string())) - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f64_unsuffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - let mut repr = n.to_string(); - if !repr.contains('.') { - repr.push_str(".0"); - } - Literal(bridge::client::Literal::float(&repr)) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f64` where the value - /// specified is the preceding part of the token and `f64` is the suffix of - /// the token. This token will always be inferred to be an `f64` in the - /// compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f64_suffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - Literal(bridge::client::Literal::f64(&n.to_string())) - } - - /// String literal. - pub fn string(string: &str) -> Literal { - Literal(bridge::client::Literal::string(string)) - } - - /// Character literal. - pub fn character(ch: char) -> Literal { - Literal(bridge::client::Literal::character(ch)) - } - - /// Byte string literal. - pub fn byte_string(bytes: &[u8]) -> Literal { - Literal(bridge::client::Literal::byte_string(bytes)) - } - - /// Returns the span encompassing this literal. - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span associated for this literal. - pub fn set_span(&mut self, span: Span) { - self.0.set_span(span.0); - } - - /// Returns a `Span` that is a subset of `self.span()` containing only the - /// source bytes in range `range`. Returns `None` if the would-be trimmed - /// span is outside the bounds of `self`. - // FIXME(SergioBenitez): check that the byte range starts and ends at a - // UTF-8 boundary of the source. otherwise, it's likely that a panic will - // occur elsewhere when the source text is printed. - // FIXME(SergioBenitez): there is no way for the user to know what - // `self.span()` actually maps to, so this method can currently only be - // called blindly. For example, `to_string()` for the character 'c' returns - // "'\u{63}'"; there is no way for the user to know whether the source text - // was 'c' or whether it was '\u{63}'. - pub fn subspan>(&self, range: R) -> Option { - self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) - } -} - -/// Parse a single literal from its stringified representation. -/// -/// In order to parse successfully, the input string must not contain anything -/// but the literal token. Specifically, it must not contain whitespace or -/// comments in addition to the literal. -/// -/// The resulting literal token will have a `Span::call_site()` span. -/// -/// NOTE: some errors may cause panics instead of returning `LexError`. We -/// reserve the right to change these errors into `LexError`s later. -impl FromStr for Literal { - type Err = LexError; - - fn from_str(src: &str) -> Result { - match bridge::client::Literal::from_str(src) { - Ok(literal) => Ok(Literal(literal)), - Err(()) => Err(LexError), - } - } -} - -/// Prints the literal as a string that should be losslessly convertible -/// back into the same literal (except for possible rounding for floating point literals). -impl fmt::Display for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.to_string()) - } -} - -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// Tracked access to environment variables. -pub mod tracked_env { - use std::env::{self, VarError}; - use std::ffi::OsStr; - - /// Retrieve an environment variable and add it to build dependency info. - /// Build system executing the compiler will know that the variable was accessed during - /// compilation, and will be able to rerun the build when the value of that variable changes. - /// Besides the dependency tracking this function should be equivalent to `env::var` from the - /// standard library, except that the argument must be UTF-8. - pub fn var + AsRef>(key: K) -> Result { - let key: &str = key.as_ref(); - let value = env::var(key); - super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); - value - } -} - -/// Tracked access to additional files. -pub mod tracked_path { - - /// Track a file explicitly. - /// - /// Commonly used for tracking asset preprocessing. - pub fn path>(path: P) { - let path: &str = path.as_ref(); - super::bridge::client::FreeFunctions::track_path(path); - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/quote.rs b/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/quote.rs deleted file mode 100644 index 39309faa4121..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/quote.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! # Quasiquoter -//! This file contains the implementation internals of the quasiquoter provided by `quote!`. - -//! This quasiquoter uses macros 2.0 hygiene to reliably access -//! items from `proc_macro`, to build a `proc_macro::TokenStream`. - -use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; - -macro_rules! quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; - (,) => { Punct::new(',', Spacing::Alone) }; - (.) => { Punct::new('.', Spacing::Alone) }; - (;) => { Punct::new(';', Spacing::Alone) }; - (!) => { Punct::new('!', Spacing::Alone) }; - (<) => { Punct::new('<', Spacing::Alone) }; - (>) => { Punct::new('>', Spacing::Alone) }; - (&) => { Punct::new('&', Spacing::Alone) }; - (=) => { Punct::new('=', Spacing::Alone) }; - ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; -} - -macro_rules! quote_ts { - ((@ $($t:tt)*)) => { $($t)* }; - (::) => { - [ - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)), - ].iter() - .cloned() - .map(|mut x| { - x.set_span(Span::def_site()); - x - }) - .collect::() - }; - ($t:tt) => { TokenTree::from(quote_tt!($t)) }; -} - -/// Simpler version of the real `quote!` macro, implemented solely -/// through `macro_rules`, for bootstrapping the real implementation -/// (see the `quote` function), which does not have access to the -/// real `quote!` macro due to the `proc_macro` crate not being -/// able to depend on itself. -/// -/// Note: supported tokens are a subset of the real `quote!`, but -/// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! quote { - () => { TokenStream::new() }; - ($($t:tt)*) => { - [ - $(TokenStream::from(quote_ts!($t)),)* - ].iter().cloned().collect::() - }; -} - -/// Quote a `TokenStream` into a `TokenStream`. -/// This is the actual implementation of the `quote!()` proc macro. -/// -/// It is loaded by the compiler in `register_builtin_macros`. -pub fn quote(stream: TokenStream) -> TokenStream { - if stream.is_empty() { - return quote!(super::TokenStream::new()); - } - let proc_macro_crate = quote!(crate); - let mut after_dollar = false; - let tokens = stream - .into_iter() - .filter_map(|tree| { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - return Some(quote!(Into::::into( - Clone::clone(&(@ tree))),)); - } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - return None; - } - } - - Some(quote!(super::TokenStream::from((@ match tree { - TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new( - (@ TokenTree::from(Literal::character(tt.as_char()))), - (@ match tt.spacing() { - Spacing::Alone => quote!(super::Spacing::Alone), - Spacing::Joint => quote!(super::Spacing::Joint), - }), - ))), - TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new( - (@ match tt.delimiter() { - Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis), - Delimiter::Brace => quote!(super::Delimiter::Brace), - Delimiter::Bracket => quote!(super::Delimiter::Bracket), - Delimiter::None => quote!(super::Delimiter::None), - }), - (@ quote(tt.stream())), - ))), - TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new( - (@ TokenTree::from(Literal::string(&tt.to_string()))), - (@ quote_span(proc_macro_crate.clone(), tt.span())), - ))), - TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({ - let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) - .parse::() - .unwrap() - .into_iter(); - if let (Some(super::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) - { - lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span()))); - lit - } else { - unreachable!() - } - })) - })),)) - }) - .collect::(); - - if after_dollar { - panic!("unexpected trailing `$` in `quote!`"); - } - - quote!([(@ tokens)].iter().cloned().collect::()) -} - -/// Quote a `Span` into a `TokenStream`. -/// This is needed to implement a custom quoter. -pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { - let id = span.save_span(); - quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_64/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_1_64/ra_server.rs deleted file mode 100644 index 7e8e67856e9f..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_64/ra_server.rs +++ /dev/null @@ -1,792 +0,0 @@ -//! Rustc proc-macro server implementation with tt -//! -//! Based on idea from -//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that -//! we could provide any TokenStream implementation. -//! The original idea from fedochet is using proc-macro2 as backend, -//! we use tt instead for better integration with RA. -//! -//! FIXME: No span and source file information is implemented yet - -use super::proc_macro::bridge::{self, server}; - -use std::collections::HashMap; -use std::hash::Hash; -use std::iter::FromIterator; -use std::ops::Bound; -use std::{ascii, vec::IntoIter}; - -type Group = tt::Subtree; -type TokenTree = tt::TokenTree; -type Punct = tt::Punct; -type Spacing = tt::Spacing; -type Literal = tt::Literal; -type Span = tt::TokenId; - -#[derive(Debug, Default, Clone)] -pub struct TokenStream { - pub token_trees: Vec, -} - -impl TokenStream { - pub fn new() -> Self { - TokenStream::default() - } - - pub fn with_subtree(subtree: tt::Subtree) -> Self { - if subtree.delimiter.is_some() { - TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } - } else { - TokenStream { token_trees: subtree.token_trees } - } - } - - pub fn into_subtree(self) -> tt::Subtree { - tt::Subtree { delimiter: None, token_trees: self.token_trees } - } - - pub fn is_empty(&self) -> bool { - self.token_trees.is_empty() - } -} - -/// Creates a token stream containing a single token tree. -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream { token_trees: vec![tree] } - } -} - -/// Collects a number of token trees into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { - let mut builder = TokenStreamBuilder::new(); - streams.into_iter().for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { - for item in streams { - for tkn in item { - match tkn { - tt::TokenTree::Subtree(subtree) if subtree.delimiter.is_none() => { - self.token_trees.extend(subtree.token_trees); - } - _ => { - self.token_trees.push(tkn); - } - } - } - } - } -} - -#[derive(Clone)] -pub struct SourceFile { - // FIXME stub -} - -type Level = super::proc_macro::Level; -type LineColumn = super::proc_macro::LineColumn; - -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec, - children: Vec, -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } -} - -// Rustc Server Ident has to be `Copyable` -// We use a stub here for bypassing -#[derive(Hash, Eq, PartialEq, Copy, Clone)] -pub struct IdentId(u32); - -#[derive(Clone, Hash, Eq, PartialEq)] -struct IdentData(tt::Ident); - -#[derive(Default)] -struct IdentInterner { - idents: HashMap, - ident_data: Vec, -} - -impl IdentInterner { - fn intern(&mut self, data: &IdentData) -> u32 { - if let Some(index) = self.idents.get(data) { - return *index; - } - - let index = self.idents.len() as u32; - self.ident_data.push(data.clone()); - self.idents.insert(data.clone(), index); - index - } - - fn get(&self, index: u32) -> &IdentData { - &self.ident_data[index as usize] - } - - #[allow(unused)] - fn get_mut(&mut self, index: u32) -> &mut IdentData { - self.ident_data.get_mut(index as usize).expect("Should be consistent") - } -} - -pub struct TokenStreamBuilder { - acc: TokenStream, -} - -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { - use std::str::FromStr; - - use super::{TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = super::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.token_trees.into_iter() - } - } - - type LexError = String; - - /// Attempts to break the string into tokens and parse those tokens into a token stream. - /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters - /// or characters not existing in the language. - /// All tokens in the parsed stream get `Span::call_site()` spans. - /// - /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to - /// change these errors into `LexError`s later. - impl FromStr for TokenStream { - type Err = LexError; - - fn from_str(src: &str) -> Result { - let (subtree, _token_map) = - mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?; - - let subtree = subtree_replace_token_ids_with_unspecified(subtree); - Ok(TokenStream::with_subtree(subtree)) - } - } - - impl ToString for TokenStream { - fn to_string(&self) -> String { - tt::pretty(&self.token_trees) - } - } - - fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { - tt::Subtree { - delimiter: subtree - .delimiter - .map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }), - token_trees: subtree - .token_trees - .into_iter() - .map(token_tree_replace_token_ids_with_unspecified) - .collect(), - } - } - - fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree { - match tt { - tt::TokenTree::Leaf(leaf) => { - tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf)) - } - tt::TokenTree::Subtree(subtree) => { - tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree)) - } - } - } - - fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf { - match leaf { - tt::Leaf::Literal(lit) => { - tt::Leaf::Literal(tt::Literal { id: tt::TokenId::unspecified(), ..lit }) - } - tt::Leaf::Punct(punct) => { - tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct }) - } - tt::Leaf::Ident(ident) => { - tt::Leaf::Ident(tt::Ident { id: tt::TokenId::unspecified(), ..ident }) - } - } - } -} - -impl TokenStreamBuilder { - fn new() -> TokenStreamBuilder { - TokenStreamBuilder { acc: TokenStream::new() } - } - - fn push(&mut self, stream: TokenStream) { - self.acc.extend(stream.into_iter()) - } - - fn build(self) -> TokenStream { - self.acc - } -} - -pub struct FreeFunctions; - -#[derive(Clone)] -pub struct TokenStreamIter { - trees: IntoIter, -} - -#[derive(Default)] -pub struct RustAnalyzer { - ident_interner: IdentInterner, - // FIXME: store span information here. -} - -impl server::Types for RustAnalyzer { - type FreeFunctions = FreeFunctions; - type TokenStream = TokenStream; - type Ident = IdentId; - type Literal = Literal; - type SourceFile = SourceFile; - type Diagnostic = Diagnostic; - type Span = Span; - type MultiSpan = Vec; -} - -impl server::FreeFunctions for RustAnalyzer { - fn track_env_var(&mut self, _var: &str, _value: Option<&str>) { - // FIXME: track env var accesses - // https://github.com/rust-lang/rust/pull/71858 - } - fn track_path(&mut self, _path: &str) {} -} - -impl server::TokenStream for RustAnalyzer { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { - stream.is_empty() - } - fn from_str(&mut self, src: &str) -> Self::TokenStream { - use std::str::FromStr; - - Self::TokenStream::from_str(src).expect("cannot parse string") - } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { - stream.to_string() - } - fn from_token_tree( - &mut self, - tree: bridge::TokenTree, - ) -> Self::TokenStream { - match tree { - bridge::TokenTree::Group(group) => { - let group = Group { - delimiter: delim_to_internal(group.delimiter), - token_trees: match group.stream { - Some(stream) => stream.into_iter().collect(), - None => Vec::new(), - }, - }; - let tree = TokenTree::from(group); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Ident(IdentId(index)) => { - let IdentData(ident) = self.ident_interner.get(index).clone(); - let ident: tt::Ident = ident; - let leaf = tt::Leaf::from(ident); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Literal(literal) => { - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Punct(p) => { - let punct = tt::Punct { - char: p.ch as char, - spacing: if p.joint { Spacing::Joint } else { Spacing::Alone }, - id: p.span, - }; - let leaf = tt::Leaf::from(punct); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - } - } - - fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { - Ok(self_.clone()) - } - - fn concat_trees( - &mut self, - base: Option, - trees: Vec>, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for tree in trees { - builder.push(self.from_token_tree(tree)); - } - builder.build() - } - - fn concat_streams( - &mut self, - base: Option, - streams: Vec, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for stream in streams { - builder.push(stream); - } - builder.build() - } - - fn into_trees( - &mut self, - stream: Self::TokenStream, - ) -> Vec> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident)))) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit), - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => { - bridge::TokenTree::Punct(bridge::Punct { - ch: punct.char as u8, - joint: punct.spacing == Spacing::Joint, - span: punct.id, - }) - } - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group { - delimiter: delim_to_external(subtree.delimiter), - stream: if subtree.token_trees.is_empty() { - None - } else { - Some(subtree.token_trees.into_iter().collect()) - }, - span: bridge::DelimSpan::from_single( - subtree.delimiter.map_or(Span::unspecified(), |del| del.id), - ), - }), - }) - .collect() - } -} - -fn delim_to_internal(d: bridge::Delimiter) -> Option { - let kind = match d { - bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, - bridge::Delimiter::Brace => tt::DelimiterKind::Brace, - bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket, - bridge::Delimiter::None => return None, - }; - Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind }) -} - -fn delim_to_external(d: Option) -> bridge::Delimiter { - match d.map(|it| it.kind) { - Some(tt::DelimiterKind::Parenthesis) => bridge::Delimiter::Parenthesis, - Some(tt::DelimiterKind::Brace) => bridge::Delimiter::Brace, - Some(tt::DelimiterKind::Bracket) => bridge::Delimiter::Bracket, - None => bridge::Delimiter::None, - } -} - -fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing { - match spacing { - bridge::Spacing::Alone => Spacing::Alone, - bridge::Spacing::Joint => Spacing::Joint, - } -} - -fn spacing_to_external(spacing: Spacing) -> bridge::Spacing { - match spacing { - Spacing::Alone => bridge::Spacing::Alone, - Spacing::Joint => bridge::Spacing::Joint, - } -} - -impl server::Ident for RustAnalyzer { - fn new(&mut self, string: &str, span: Self::Span, _is_raw: bool) -> Self::Ident { - IdentId(self.ident_interner.intern(&IdentData(tt::Ident { text: string.into(), id: span }))) - } - - fn span(&mut self, ident: Self::Ident) -> Self::Span { - self.ident_interner.get(ident.0).0.id - } - fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { - let data = self.ident_interner.get(ident.0); - let new = IdentData(tt::Ident { id: span, ..data.0.clone() }); - IdentId(self.ident_interner.intern(&new)) - } -} - -impl server::Literal for RustAnalyzer { - fn debug_kind(&mut self, _literal: &Self::Literal) -> String { - // r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these. - // They must still be present to be ABI-compatible and work with upstream proc_macro. - "".to_owned() - } - fn from_str(&mut self, s: &str) -> Result { - Ok(Literal { text: s.into(), id: tt::TokenId::unspecified() }) - } - fn symbol(&mut self, literal: &Self::Literal) -> String { - literal.text.to_string() - } - fn suffix(&mut self, _literal: &Self::Literal) -> Option { - None - } - - fn to_string(&mut self, literal: &Self::Literal) -> String { - literal.to_string() - } - - fn integer(&mut self, n: &str) -> Self::Literal { - let n = match n.parse::() { - Ok(n) => n.to_string(), - Err(_) => n.parse::().unwrap().to_string(), - }; - Literal { text: n.into(), id: tt::TokenId::unspecified() } - } - - fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { - macro_rules! def_suffixed_integer { - ($kind:ident, $($ty:ty),*) => { - match $kind { - $( - stringify!($ty) => { - let n: $ty = n.parse().unwrap(); - format!(concat!("{}", stringify!($ty)), n) - } - )* - _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind), - } - } - } - - let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize}; - - Literal { text: text.into(), id: tt::TokenId::unspecified() } - } - - fn float(&mut self, n: &str) -> Self::Literal { - let n: f64 = n.parse().unwrap(); - let mut text = f64::to_string(&n); - if !text.contains('.') { - text += ".0" - } - Literal { text: text.into(), id: tt::TokenId::unspecified() } - } - - fn f32(&mut self, n: &str) -> Self::Literal { - let n: f32 = n.parse().unwrap(); - let text = format!("{}f32", n); - Literal { text: text.into(), id: tt::TokenId::unspecified() } - } - - fn f64(&mut self, n: &str) -> Self::Literal { - let n: f64 = n.parse().unwrap(); - let text = format!("{}f64", n); - Literal { text: text.into(), id: tt::TokenId::unspecified() } - } - - fn string(&mut self, string: &str) -> Self::Literal { - let mut escaped = String::new(); - for ch in string.chars() { - escaped.extend(ch.escape_debug()); - } - Literal { text: format!("\"{}\"", escaped).into(), id: tt::TokenId::unspecified() } - } - - fn character(&mut self, ch: char) -> Self::Literal { - Literal { text: format!("'{}'", ch).into(), id: tt::TokenId::unspecified() } - } - - fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { - let string = bytes - .iter() - .cloned() - .flat_map(ascii::escape_default) - .map(Into::::into) - .collect::(); - - Literal { text: format!("b\"{}\"", string).into(), id: tt::TokenId::unspecified() } - } - - fn span(&mut self, literal: &Self::Literal) -> Self::Span { - literal.id - } - - fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { - literal.id = span; - } - - fn subspan( - &mut self, - _literal: &Self::Literal, - _start: Bound, - _end: Bound, - ) -> Option { - // FIXME handle span - None - } -} - -impl server::SourceFile for RustAnalyzer { - // FIXME these are all stubs - fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { - true - } - fn path(&mut self, _file: &Self::SourceFile) -> String { - String::new() - } - fn is_real(&mut self, _file: &Self::SourceFile) -> bool { - true - } -} - -impl server::Diagnostic for RustAnalyzer { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level, msg); - diag.spans = spans; - diag - } - - fn sub( - &mut self, - _diag: &mut Self::Diagnostic, - _level: Level, - _msg: &str, - _spans: Self::MultiSpan, - ) { - // FIXME handle diagnostic - // - } - - fn emit(&mut self, _diag: Self::Diagnostic) { - // FIXME handle diagnostic - // diag.emit() - } -} - -impl server::Span for RustAnalyzer { - fn debug(&mut self, span: Self::Span) -> String { - format!("{:?}", span.0) - } - fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { - SourceFile {} - } - fn save_span(&mut self, _span: Self::Span) -> usize { - // FIXME stub - 0 - } - fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { - // FIXME stub - tt::TokenId::unspecified() - } - /// Recent feature, not yet in the proc_macro - /// - /// See PR: - /// https://github.com/rust-lang/rust/pull/55780 - fn source_text(&mut self, _span: Self::Span) -> Option { - None - } - - fn parent(&mut self, _span: Self::Span) -> Option { - // FIXME handle span - None - } - fn source(&mut self, span: Self::Span) -> Self::Span { - // FIXME handle span - span - } - fn start(&mut self, _span: Self::Span) -> LineColumn { - // FIXME handle span - LineColumn { line: 0, column: 0 } - } - fn end(&mut self, _span: Self::Span) -> LineColumn { - // FIXME handle span - LineColumn { line: 0, column: 0 } - } - fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option { - // Just return the first span again, because some macros will unwrap the result. - Some(first) - } - fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { - // FIXME handle span - tt::TokenId::unspecified() - } - - fn after(&mut self, _self_: Self::Span) -> Self::Span { - tt::TokenId::unspecified() - } - - fn before(&mut self, _self_: Self::Span) -> Self::Span { - tt::TokenId::unspecified() - } -} - -impl server::MultiSpan for RustAnalyzer { - fn new(&mut self) -> Self::MultiSpan { - // FIXME handle span - vec![] - } - - fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) { - //TODP - other.push(span) - } -} - -impl server::Server for RustAnalyzer { - fn globals(&mut self) -> bridge::ExpnGlobals { - bridge::ExpnGlobals { - def_site: Span::unspecified(), - call_site: Span::unspecified(), - mixed_site: Span::unspecified(), - } - } -} - -#[cfg(test)] -mod tests { - use super::super::proc_macro::bridge::server::Literal; - use super::*; - - #[test] - fn test_ra_server_literals() { - let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() }; - assert_eq!(srv.integer("1234").text, "1234"); - - assert_eq!(srv.typed_integer("12", "u8").text, "12u8"); - assert_eq!(srv.typed_integer("255", "u16").text, "255u16"); - assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32"); - assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64"); - assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128"); - assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize"); - assert_eq!(srv.typed_integer("127", "i8").text, "127i8"); - assert_eq!(srv.typed_integer("255", "i16").text, "255i16"); - assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32"); - assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64"); - assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128"); - assert_eq!(srv.float("0").text, "0.0"); - assert_eq!(srv.float("15684.5867").text, "15684.5867"); - assert_eq!(srv.f32("15684.58").text, "15684.58f32"); - assert_eq!(srv.f64("15684.58").text, "15684.58f64"); - - assert_eq!(srv.string("hello_world").text, "\"hello_world\""); - assert_eq!(srv.character('c').text, "'c'"); - assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); - - // u128::max - assert_eq!( - srv.integer("340282366920938463463374607431768211455").text, - "340282366920938463463374607431768211455" - ); - // i128::min - assert_eq!( - srv.integer("-170141183460469231731687303715884105728").text, - "-170141183460469231731687303715884105728" - ); - } - - #[test] - fn test_ra_server_to_string() { - let s = TokenStream { - token_trees: vec![ - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "struct".into(), - id: tt::TokenId::unspecified(), - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "T".into(), - id: tt::TokenId::unspecified(), - })), - tt::TokenTree::Subtree(tt::Subtree { - delimiter: Some(tt::Delimiter { - id: tt::TokenId::unspecified(), - kind: tt::DelimiterKind::Brace, - }), - token_trees: vec![], - }), - ], - }; - - assert_eq!(s.to_string(), "struct T {}"); - } - - #[test] - fn test_ra_server_from_str() { - use std::str::FromStr; - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: Some(tt::Delimiter { - id: tt::TokenId::unspecified(), - kind: tt::DelimiterKind::Parenthesis, - }), - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "a".into(), - id: tt::TokenId::unspecified(), - }))], - }); - - let t1 = TokenStream::from_str("(a)").unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); - - let t2 = TokenStream::from_str("(a);").unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); - - let underscore = TokenStream::from_str("_").unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "_".into(), - id: tt::TokenId::unspecified(), - })) - ); - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs index 46882845a807..52eb7ce17d6a 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs @@ -20,7 +20,7 @@ use token_stream::TokenStreamBuilder; mod symbol; pub use symbol::*; -use std::{iter::FromIterator, ops::Bound}; +use std::ops::Bound; type Group = tt::Subtree; type TokenTree = tt::TokenTree; diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs index 705d09ea9458..f7d3a30919e1 100644 --- a/crates/proc-macro-srv/src/abis/mod.rs +++ b/crates/proc-macro-srv/src/abis/mod.rs @@ -25,7 +25,6 @@ mod abi_1_58; mod abi_1_63; -mod abi_1_64; #[cfg(feature = "sysroot-abi")] mod abi_sysroot; @@ -34,12 +33,11 @@ include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); // Used by `test/utils.rs` #[cfg(test)] -pub(crate) use abi_1_64::TokenStream as TestTokenStream; +pub(crate) use abi_1_63::TokenStream as TestTokenStream; use super::dylib::LoadProcMacroDylibError; pub(crate) use abi_1_58::Abi as Abi_1_58; pub(crate) use abi_1_63::Abi as Abi_1_63; -pub(crate) use abi_1_64::Abi as Abi_1_64; #[cfg(feature = "sysroot-abi")] pub(crate) use abi_sysroot::Abi as Abi_Sysroot; use libloading::Library; @@ -58,7 +56,6 @@ impl PanicMessage { pub(crate) enum Abi { Abi1_58(Abi_1_58), Abi1_63(Abi_1_63), - Abi1_64(Abi_1_64), #[cfg(feature = "sysroot-abi")] AbiSysroot(Abi_Sysroot), } @@ -120,10 +117,6 @@ impl Abi { let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?; Ok(Abi::Abi1_63(inner)) } - (1, 64..) => { - let inner = unsafe { Abi_1_64::from_lib(lib, symbol_name) }?; - Ok(Abi::Abi1_64(inner)) - } _ => Err(LoadProcMacroDylibError::UnsupportedABI), } } @@ -137,7 +130,6 @@ impl Abi { match self { Self::Abi1_58(abi) => abi.expand(macro_name, macro_body, attributes), Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes), - Self::Abi1_64(abi) => abi.expand(macro_name, macro_body, attributes), #[cfg(feature = "sysroot-abi")] Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes), } @@ -147,7 +139,6 @@ impl Abi { match self { Self::Abi1_58(abi) => abi.list_macros(), Self::Abi1_63(abi) => abi.list_macros(), - Self::Abi1_64(abi) => abi.list_macros(), #[cfg(feature = "sysroot-abi")] Self::AbiSysroot(abi) => abi.list_macros(), } diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 2b6c070fece3..7aba74e5396d 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -1,7 +1,6 @@ //! Handles dynamic library loading for proc macro use std::{ - convert::TryInto, fmt, fs::File, io, diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index 07222907f088..6339d56d0179 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -19,7 +19,7 @@ fn test_derive_error() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [alone] 4294967295 + PUNCH ! [joint] 4294967295 SUBTREE () 4294967295 LITERAL "#[derive(DeriveError)] struct S ;" 4294967295 PUNCH ; [alone] 4294967295"##]], @@ -109,7 +109,7 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 4294967295 LITERAL 2_u32 4294967295 PUNCH , [alone] 4294967295 - PUNCH - [alone] 4294967295 + PUNCH - [joint] 4294967295 LITERAL 4i64 4294967295 PUNCH , [alone] 4294967295 LITERAL 3.14f32 4294967295 @@ -130,7 +130,7 @@ fn test_attr_macro() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [alone] 4294967295 + PUNCH ! [joint] 4294967295 SUBTREE () 4294967295 LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295 PUNCH ; [alone] 4294967295"##]], diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index ee7f8339a76a..84e772d1684a 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -12,6 +12,7 @@ use cargo_metadata::{camino::Utf8Path, Message}; use la_arena::ArenaMap; use paths::AbsPathBuf; use rustc_hash::FxHashMap; +use semver::Version; use serde::Deserialize; use crate::{cfg_flag::CfgFlag, CargoConfig, CargoWorkspace, Package}; @@ -77,9 +78,32 @@ impl WorkspaceBuildScripts { config: &CargoConfig, workspace: &CargoWorkspace, progress: &dyn Fn(String), + toolchain: &Option, ) -> io::Result { - let mut cmd = Self::build_command(config); + const RUST_1_62: Version = Version::new(1, 62, 0); + match Self::run_(Self::build_command(config), config, workspace, progress) { + Ok(WorkspaceBuildScripts { error: Some(error), .. }) + if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_62) => + { + // building build scripts failed, attempt to build with --keep-going so + // that we potentially get more build data + let mut cmd = Self::build_command(config); + cmd.args(&["-Z", "unstable-options", "--keep-going"]).env("RUSTC_BOOTSTRAP", "1"); + let mut res = Self::run_(cmd, config, workspace, progress)?; + res.error = Some(error); + Ok(res) + } + res => res, + } + } + + fn run_( + mut cmd: Command, + config: &CargoConfig, + workspace: &CargoWorkspace, + progress: &dyn Fn(String), + ) -> io::Result { if config.wrap_rustc_in_build_scripts { // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use // that to compile only proc macros and build scripts during the initial diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index e304a59c0180..8d0fa757c2e1 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -28,6 +28,7 @@ fn load_cargo_with_overrides(file: &str, cfg_overrides: CfgOverrides) -> CrateGr rustc: None, rustc_cfg: Vec::new(), cfg_overrides, + toolchain: None, }; to_crate_graph(project_workspace) } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index b144006b44e0..daabb299f76c 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -12,6 +12,7 @@ use base_db::{ use cfg::{CfgDiff, CfgOptions}; use paths::{AbsPath, AbsPathBuf}; use rustc_hash::{FxHashMap, FxHashSet}; +use semver::Version; use stdx::always; use crate::{ @@ -77,6 +78,7 @@ pub enum ProjectWorkspace { /// different target. rustc_cfg: Vec, cfg_overrides: CfgOverrides, + toolchain: Option, }, /// Project workspace was manually specified using a `rust-project.json` file. Json { project: ProjectJson, sysroot: Option, rustc_cfg: Vec }, @@ -105,6 +107,7 @@ impl fmt::Debug for ProjectWorkspace { rustc, rustc_cfg, cfg_overrides, + toolchain, } => f .debug_struct("Cargo") .field("root", &cargo.workspace_root().file_name()) @@ -116,6 +119,7 @@ impl fmt::Debug for ProjectWorkspace { ) .field("n_rustc_cfg", &rustc_cfg.len()) .field("n_cfg_overrides", &cfg_overrides.len()) + .field("toolchain", &toolchain) .finish(), ProjectWorkspace::Json { project, sysroot, rustc_cfg } => { let mut debug_struct = f.debug_struct("Json"); @@ -160,6 +164,9 @@ impl ProjectWorkspace { cmd.arg("--version"); cmd })?; + let toolchain = cargo_version + .get("cargo ".len()..) + .and_then(|it| Version::parse(it.split_whitespace().next()?).ok()); let meta = CargoWorkspace::fetch_metadata( &cargo_toml, @@ -169,9 +176,9 @@ impl ProjectWorkspace { ) .with_context(|| { format!( - "Failed to read Cargo metadata from Cargo.toml file {}, {}", + "Failed to read Cargo metadata from Cargo.toml file {}, {:?}", cargo_toml.display(), - cargo_version + toolchain ) })?; let cargo = CargoWorkspace::new(meta); @@ -219,6 +226,7 @@ impl ProjectWorkspace { rustc, rustc_cfg, cfg_overrides, + toolchain, } } }; @@ -271,8 +279,8 @@ impl ProjectWorkspace { progress: &dyn Fn(String), ) -> Result { match self { - ProjectWorkspace::Cargo { cargo, .. } => { - WorkspaceBuildScripts::run(config, cargo, progress).with_context(|| { + ProjectWorkspace::Cargo { cargo, toolchain, .. } => { + WorkspaceBuildScripts::run(config, cargo, progress, toolchain).with_context(|| { format!("Failed to run build scripts for {}", &cargo.workspace_root().display()) }) } @@ -320,6 +328,7 @@ impl ProjectWorkspace { rustc_cfg: _, cfg_overrides: _, build_scripts, + toolchain: _, } => { cargo .packages() @@ -425,6 +434,7 @@ impl ProjectWorkspace { rustc_cfg, cfg_overrides, build_scripts, + toolchain: _, } => cargo_to_crate_graph( rustc_cfg.clone(), cfg_overrides, diff --git a/crates/rust-analyzer/src/bin/rustc_wrapper.rs b/crates/rust-analyzer/src/bin/rustc_wrapper.rs index 2f6d4706d879..38e9c7dd7e11 100644 --- a/crates/rust-analyzer/src/bin/rustc_wrapper.rs +++ b/crates/rust-analyzer/src/bin/rustc_wrapper.rs @@ -17,6 +17,11 @@ pub(crate) fn run_rustc_skipping_cargo_checking( rustc_executable: OsString, args: Vec, ) -> io::Result { + // `CARGO_CFG_TARGET_ARCH` is only set by cargo when executing build scripts + // We don't want to exit out checks unconditionally with success if a build + // script tries to invoke checks themselves + // See https://github.com/rust-lang/rust-analyzer/issues/12973 for context + let not_invoked_by_build_script = std::env::var_os("CARGO_CFG_TARGET_ARCH").is_none(); let is_cargo_check = args.iter().any(|arg| { let arg = arg.to_string_lossy(); // `cargo check` invokes `rustc` with `--emit=metadata` argument. @@ -29,7 +34,7 @@ pub(crate) fn run_rustc_skipping_cargo_checking( // The default output filename is CRATE_NAME.rmeta. arg.starts_with("--emit=") && arg.contains("metadata") && !arg.contains("link") }); - if is_cargo_check { + if not_invoked_by_build_script && is_cargo_check { return Ok(ExitCode(Some(0))); } run_rustc(rustc_executable, args) diff --git a/crates/rust-analyzer/src/cli/diagnostics.rs b/crates/rust-analyzer/src/cli/diagnostics.rs index 52511ceb5805..247007db0a78 100644 --- a/crates/rust-analyzer/src/cli/diagnostics.rs +++ b/crates/rust-analyzer/src/cli/diagnostics.rs @@ -43,7 +43,7 @@ impl flags::Diagnostics { println!("processing crate: {}, module: {}", crate_name, _vfs.file_path(file_id)); for diagnostic in analysis .diagnostics( - &DiagnosticsConfig::default(), + &DiagnosticsConfig::test_sample(), AssistResolveStrategy::None, file_id, ) diff --git a/crates/rust-analyzer/src/cli/flags.rs b/crates/rust-analyzer/src/cli/flags.rs index 19907ebddb6a..080e2fb44388 100644 --- a/crates/rust-analyzer/src/cli/flags.rs +++ b/crates/rust-analyzer/src/cli/flags.rs @@ -10,6 +10,10 @@ xflags::xflags! { src "./src/cli/flags.rs" /// LSP server for the Rust programming language. + /// + /// Subcommands and their flags do not provide any stability guarantees and may be removed or + /// changed without notice. Top-level flags that are not are marked as [Unstable] provide + /// backwards-compatibility and may be relied on. cmd rust-analyzer { /// Verbosity level, can be repeated multiple times. repeated -v, --verbose @@ -21,7 +25,7 @@ xflags::xflags! { /// Flush log records to the file immediately. optional --no-log-buffering - /// Wait until a debugger is attached to (requires debug build). + /// [Unstable] Wait until a debugger is attached to (requires debug build). optional --wait-dbg default cmd lsp-server { diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index ac0fdf85a774..1629c1dd328a 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -881,6 +881,7 @@ impl Config { ExprFillDefaultDef::Todo => ExprFillDefaultMode::Todo, ExprFillDefaultDef::Default => ExprFillDefaultMode::Default, }, + insert_use: self.insert_use_config(), } } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index cff4bd7f66ac..74689fd8757f 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -512,7 +512,7 @@ fn clippy_code_description(code: Option<&str>) -> Option self.complete_request(resp), }, - Event::Task(mut task) => { + Event::Task(task) => { let _p = profile::span("GlobalState::handle_event/task"); let mut prime_caches_progress = Vec::new(); - loop { - match task { - Task::Response(response) => self.respond(response), - Task::Retry(req) => self.on_request(req), - Task::Diagnostics(diagnostics_per_file) => { - for (file_id, diagnostics) in diagnostics_per_file { - self.diagnostics.set_native_diagnostics(file_id, diagnostics) - } - } - Task::PrimeCaches(progress) => match progress { - PrimeCachesProgress::Begin => prime_caches_progress.push(progress), - PrimeCachesProgress::Report(_) => { - match prime_caches_progress.last_mut() { - Some(last @ PrimeCachesProgress::Report(_)) => { - // Coalesce subsequent update events. - *last = progress; - } - _ => prime_caches_progress.push(progress), - } - } - PrimeCachesProgress::End { .. } => prime_caches_progress.push(progress), - }, - Task::FetchWorkspace(progress) => { - let (state, msg) = match progress { - ProjectWorkspaceProgress::Begin => (Progress::Begin, None), - ProjectWorkspaceProgress::Report(msg) => { - (Progress::Report, Some(msg)) - } - ProjectWorkspaceProgress::End(workspaces) => { - self.fetch_workspaces_queue.op_completed(workspaces); - let old = Arc::clone(&self.workspaces); - self.switch_workspaces("fetched workspace".to_string()); - let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces); - - if self.config.run_build_scripts() && workspaces_updated { - self.fetch_build_data_queue - .request_op(format!("workspace updated")); - } - - (Progress::End, None) - } - }; - - self.report_progress("Fetching", state, msg, None); - } - Task::FetchBuildData(progress) => { - let (state, msg) = match progress { - BuildDataProgress::Begin => (Some(Progress::Begin), None), - BuildDataProgress::Report(msg) => { - (Some(Progress::Report), Some(msg)) - } - BuildDataProgress::End(build_data_result) => { - self.fetch_build_data_queue.op_completed(build_data_result); - - self.switch_workspaces("fetched build data".to_string()); - - (Some(Progress::End), None) - } - }; - - if let Some(state) = state { - self.report_progress("Loading", state, msg, None); - } - } - } - - // Coalesce multiple task events into one loop turn - task = match self.task_pool.receiver.try_recv() { - Ok(task) => task, - Err(_) => break, - }; + self.handle_task(&mut prime_caches_progress, task); + // Coalesce multiple task events into one loop turn + while let Ok(task) = self.task_pool.receiver.try_recv() { + self.handle_task(&mut prime_caches_progress, task); } for progress in prime_caches_progress { @@ -326,119 +260,20 @@ impl GlobalState { self.report_progress("Indexing", state, message, Some(fraction)); } } - Event::Vfs(mut task) => { + Event::Vfs(message) => { let _p = profile::span("GlobalState::handle_event/vfs"); - loop { - match task { - vfs::loader::Message::Loaded { files } => { - let vfs = &mut self.vfs.write().0; - for (path, contents) in files { - let path = VfsPath::from(path); - if !self.mem_docs.contains(&path) { - vfs.set_file_contents(path, contents); - } - } - } - vfs::loader::Message::Progress { n_total, n_done, config_version } => { - always!(config_version <= self.vfs_config_version); - - self.vfs_progress_config_version = config_version; - self.vfs_progress_n_total = n_total; - self.vfs_progress_n_done = n_done; - - let state = if n_done == 0 { - Progress::Begin - } else if n_done < n_total { - Progress::Report - } else { - assert_eq!(n_done, n_total); - Progress::End - }; - self.report_progress( - "Roots Scanned", - state, - Some(format!("{}/{}", n_done, n_total)), - Some(Progress::fraction(n_done, n_total)), - ) - } - } - // Coalesce many VFS event into a single loop turn - task = match self.loader.receiver.try_recv() { - Ok(task) => task, - Err(_) => break, - } + self.handle_vfs_msg(message); + // Coalesce many VFS event into a single loop turn + while let Ok(message) = self.loader.receiver.try_recv() { + self.handle_vfs_msg(message); } } - Event::Flycheck(mut task) => { + Event::Flycheck(message) => { let _p = profile::span("GlobalState::handle_event/flycheck"); - loop { - match task { - flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { - let snap = self.snapshot(); - let diagnostics = - crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( - &self.config.diagnostics_map(), - &diagnostic, - &workspace_root, - &snap, - ); - for diag in diagnostics { - match url_to_file_id(&self.vfs.read().0, &diag.url) { - Ok(file_id) => self.diagnostics.add_check_diagnostic( - id, - file_id, - diag.diagnostic, - diag.fix, - ), - Err(err) => { - tracing::error!( - "File with cargo diagnostic not found in VFS: {}", - err - ); - } - }; - } - } - - flycheck::Message::Progress { id, progress } => { - let (state, message) = match progress { - flycheck::Progress::DidStart => { - self.diagnostics.clear_check(id); - (Progress::Begin, None) - } - flycheck::Progress::DidCheckCrate(target) => { - (Progress::Report, Some(target)) - } - flycheck::Progress::DidCancel => (Progress::End, None), - flycheck::Progress::DidFinish(result) => { - if let Err(err) = result { - self.show_and_log_error( - "cargo check failed".to_string(), - Some(err.to_string()), - ); - } - (Progress::End, None) - } - }; - - // 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 { - match self.config.flycheck() { - Some(config) => format!("{}", config), - None => "cargo check".to_string(), - } - } else { - format!("cargo check (#{})", id + 1) - }; - self.report_progress(&title, state, message, None); - } - } - // Coalesce many flycheck updates into a single loop turn - task = match self.flycheck_receiver.try_recv() { - Ok(task) => task, - Err(_) => break, - } + self.handle_flycheck_msg(message); + // Coalesce many flycheck updates into a single loop turn + while let Ok(message) = self.flycheck_receiver.try_recv() { + self.handle_flycheck_msg(message); } } } @@ -447,13 +282,13 @@ impl GlobalState { let memdocs_added_or_removed = self.mem_docs.take_changes(); if self.is_quiescent() { - if !was_quiescent - && !self.fetch_workspaces_queue.op_requested() - && !self.fetch_build_data_queue.op_requested() - { - for flycheck in &self.flycheck { - flycheck.update(); - } + let became_quiescent = !(was_quiescent + || self.fetch_workspaces_queue.op_requested() + || self.fetch_build_data_queue.op_requested()); + + if became_quiescent { + // Project has loaded properly, kick off initial flycheck + self.flycheck.iter().for_each(FlycheckHandle::update); if self.config.prefill_caches() { self.prime_caches_queue.request_op("became quiescent".to_string()); } @@ -492,28 +327,15 @@ impl GlobalState { continue; } - let url = file_id_to_url(&self.vfs.read().0, file_id); - let mut diagnostics = + let uri = file_id_to_url(&self.vfs.read().0, file_id); + let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect::>(); - // https://github.com/rust-lang/rust-analyzer/issues/11404 - for d in &mut diagnostics { - if d.message.is_empty() { - d.message = " ".to_string(); - } - if let Some(rds) = d.related_information.as_mut() { - for rd in rds { - if rd.message.is_empty() { - rd.message = " ".to_string(); - } - } - } - } - let version = from_proto::vfs_path(&url) + let version = from_proto::vfs_path(&uri) .map(|path| self.mem_docs.get(&path).map(|it| it.version)) .unwrap_or_default(); self.send_notification::( - lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version }, + lsp_types::PublishDiagnosticsParams { uri, diagnostics, version }, ); } } @@ -575,11 +397,171 @@ impl GlobalState { Ok(()) } + fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) { + match task { + Task::Response(response) => self.respond(response), + Task::Retry(req) => self.on_request(req), + Task::Diagnostics(diagnostics_per_file) => { + for (file_id, diagnostics) in diagnostics_per_file { + self.diagnostics.set_native_diagnostics(file_id, diagnostics) + } + } + Task::PrimeCaches(progress) => match progress { + PrimeCachesProgress::Begin => prime_caches_progress.push(progress), + PrimeCachesProgress::Report(_) => { + match prime_caches_progress.last_mut() { + Some(last @ PrimeCachesProgress::Report(_)) => { + // Coalesce subsequent update events. + *last = progress; + } + _ => prime_caches_progress.push(progress), + } + } + PrimeCachesProgress::End { .. } => prime_caches_progress.push(progress), + }, + Task::FetchWorkspace(progress) => { + let (state, msg) = match progress { + ProjectWorkspaceProgress::Begin => (Progress::Begin, None), + ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)), + ProjectWorkspaceProgress::End(workspaces) => { + self.fetch_workspaces_queue.op_completed(workspaces); + + let old = Arc::clone(&self.workspaces); + self.switch_workspaces("fetched workspace".to_string()); + let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces); + + if self.config.run_build_scripts() && workspaces_updated { + self.fetch_build_data_queue.request_op(format!("workspace updated")); + } + + (Progress::End, None) + } + }; + + self.report_progress("Fetching", state, msg, None); + } + Task::FetchBuildData(progress) => { + let (state, msg) = match progress { + BuildDataProgress::Begin => (Some(Progress::Begin), None), + BuildDataProgress::Report(msg) => (Some(Progress::Report), Some(msg)), + BuildDataProgress::End(build_data_result) => { + self.fetch_build_data_queue.op_completed(build_data_result); + + self.switch_workspaces("fetched build data".to_string()); + + (Some(Progress::End), None) + } + }; + + if let Some(state) = state { + self.report_progress("Loading", state, msg, None); + } + } + } + } + + fn handle_vfs_msg(&mut self, message: vfs::loader::Message) { + match message { + vfs::loader::Message::Loaded { files } => { + let vfs = &mut self.vfs.write().0; + for (path, contents) in files { + let path = VfsPath::from(path); + if !self.mem_docs.contains(&path) { + vfs.set_file_contents(path, contents); + } + } + } + vfs::loader::Message::Progress { n_total, n_done, config_version } => { + always!(config_version <= self.vfs_config_version); + + self.vfs_progress_config_version = config_version; + self.vfs_progress_n_total = n_total; + self.vfs_progress_n_done = n_done; + + let state = if n_done == 0 { + Progress::Begin + } else if n_done < n_total { + Progress::Report + } else { + assert_eq!(n_done, n_total); + Progress::End + }; + self.report_progress( + "Roots Scanned", + state, + Some(format!("{}/{}", n_done, n_total)), + Some(Progress::fraction(n_done, n_total)), + ) + } + } + } + + fn handle_flycheck_msg(&mut self, message: flycheck::Message) { + match message { + flycheck::Message::AddDiagnostic { id, workspace_root, diagnostic } => { + let snap = self.snapshot(); + let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp( + &self.config.diagnostics_map(), + &diagnostic, + &workspace_root, + &snap, + ); + for diag in diagnostics { + match url_to_file_id(&self.vfs.read().0, &diag.url) { + Ok(file_id) => self.diagnostics.add_check_diagnostic( + id, + file_id, + diag.diagnostic, + diag.fix, + ), + Err(err) => { + tracing::error!("File with cargo diagnostic not found in VFS: {}", err); + } + }; + } + } + + flycheck::Message::Progress { id, progress } => { + let (state, message) = match progress { + flycheck::Progress::DidStart => { + self.diagnostics.clear_check(id); + (Progress::Begin, None) + } + flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), + flycheck::Progress::DidCancel => (Progress::End, None), + flycheck::Progress::DidFinish(result) => { + if let Err(err) = result { + self.show_and_log_error( + "cargo check failed".to_string(), + Some(err.to_string()), + ); + } + (Progress::End, None) + } + }; + + // 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 { + match self.config.flycheck() { + Some(config) => format!("{}", config), + None => "cargo check".to_string(), + } + } else { + format!("cargo check (#{})", id + 1) + }; + self.report_progress(&title, state, message, None); + } + } + } + + /// Registers and handles a request. This should only be called once per incoming request. fn on_new_request(&mut self, request_received: Instant, req: Request) { self.register_request(&req, request_received); self.on_request(req); } + /// Handles a request. fn on_request(&mut self, req: Request) { if self.shutdown_requested { self.respond(lsp_server::Response::new_err( @@ -670,6 +652,7 @@ impl GlobalState { .finish(); } + /// Handles an incoming notification. fn on_notification(&mut self, not: Notification) -> Result<()> { NotificationDispatcher { not: Some(not), global_state: self } .on::(|this, params| { @@ -743,6 +726,8 @@ impl GlobalState { let mut updated = false; if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { let (vfs, _) = &*this.vfs.read(); + + // Trigger flychecks for all workspaces that depend on the saved file if let Some(file_id) = vfs.file_id(&vfs_path) { let analysis = this.analysis_host.analysis(); // Crates containing or depending on the saved file @@ -800,6 +785,8 @@ impl GlobalState { } } } + + // Re-fetch workspaces if a workspace related file has changed if let Some(abs_path) = vfs_path.as_path() { if reload::should_refresh_for_change(&abs_path, ChangeKind::Modify) { this.fetch_workspaces_queue @@ -807,6 +794,8 @@ impl GlobalState { } } } + + // No specific flycheck was triggered, so let's trigger all of them. if !updated { for flycheck in &this.flycheck { flycheck.update(); diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 49ccad71a10e..ceb2a6d703d9 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -219,6 +219,7 @@ impl GlobalState { cfg_overrides, build_scripts: _, + toolchain: _, } => Some((cargo, sysroot, rustc, rustc_cfg, cfg_overrides)), _ => None, }; diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 7f4fa57fa1e0..e7115b0732e2 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -1386,7 +1386,7 @@ fn main() { #[test] #[cfg(target_os = "windows")] fn test_lowercase_drive_letter() { - use std::{convert::TryInto, path::Path}; + use std::path::Path; let url = url_from_abs_path(Path::new("C:\\Test").try_into().unwrap()); assert_eq!(url.to_string(), "file:///c:/Test"); diff --git a/crates/syntax/src/ast/operators.rs b/crates/syntax/src/ast/operators.rs index a687ba0b77a5..8f7b3fb60087 100644 --- a/crates/syntax/src/ast/operators.rs +++ b/crates/syntax/src/ast/operators.rs @@ -111,10 +111,10 @@ impl fmt::Display for BinaryOp { BinaryOp::ArithOp(op) => fmt::Display::fmt(op, f), BinaryOp::CmpOp(op) => fmt::Display::fmt(op, f), BinaryOp::Assignment { op } => { - f.write_str("=")?; if let Some(op) = op { fmt::Display::fmt(op, f)?; } + f.write_str("=")?; Ok(()) } } diff --git a/crates/syntax/src/fuzz.rs b/crates/syntax/src/fuzz.rs index 256999fe09cf..7c7a60d62994 100644 --- a/crates/syntax/src/fuzz.rs +++ b/crates/syntax/src/fuzz.rs @@ -2,10 +2,7 @@ //! //! We don't normally run fuzzying, so this is hopelessly bitrotten :( -use std::{ - convert::TryInto, - str::{self, FromStr}, -}; +use std::str::{self, FromStr}; use text_edit::Indel; diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index 0436681b1a0a..3ff4b6897a16 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -1,8 +1,3956 @@ { "name": "rust-analyzer", "version": "0.5.0-dev", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "rust-analyzer", + "version": "0.5.0-dev", + "license": "MIT OR Apache-2.0", + "dependencies": { + "d3": "^7.6.1", + "d3-graphviz": "^4.1.1", + "vscode-languageclient": "^8.0.0-next.14" + }, + "devDependencies": { + "@types/node": "~16.11.7", + "@types/vscode": "~1.66.0", + "@typescript-eslint/eslint-plugin": "^5.30.5", + "@typescript-eslint/parser": "^5.30.5", + "@vscode/test-electron": "^2.1.5", + "cross-env": "^7.0.3", + "esbuild": "^0.14.48", + "eslint": "^8.19.0", + "eslint-config-prettier": "^8.5.0", + "ovsx": "^0.5.1", + "prettier": "^2.7.1", + "tslib": "^2.4.0", + "typescript": "^4.7.4", + "vsce": "^2.9.2" + }, + "engines": { + "vscode": "^1.66.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.0.tgz", + "integrity": "sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.3.2", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hpcc-js/wasm": { + "version": "1.12.8", + "resolved": "https://registry.npmjs.org/@hpcc-js/wasm/-/wasm-1.12.8.tgz", + "integrity": "sha512-n4q9ARKco2hpCLsuVaW6Az3cDVaua7B3DSONHkc49WtEzgY/btvcDG5Zr1P6PZDv0sQ7oPnAi9Y+W2DI++MgcQ==", + "dependencies": { + "yargs": "^17.3.1" + }, + "bin": { + "dot-wasm": "bin/cli.js" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "16.11.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.43.tgz", + "integrity": "sha512-GqWykok+3uocgfAJM8imbozrqLnPyTrpFlrryURQlw1EesPUCx5XxTiucWDSFF9/NUEXDuD4bnvHm8xfVGWTpQ==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.66.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.66.0.tgz", + "integrity": "sha512-ZfJck4M7nrGasfs4A4YbUoxis3Vu24cETw3DERsNYtDZmYSYtk6ljKexKFKhImO/ZmY6ZMsmegu2FPkXoUFImA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.30.5.tgz", + "integrity": "sha512-lftkqRoBvc28VFXEoRgyZuztyVUQ04JvUnATSPtIRFAccbXTWL6DEtXGYMcbg998kXw1NLUJm7rTQ9eUt+q6Ig==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/type-utils": "5.30.5", + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", + "functional-red-black-tree": "^1.0.1", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.30.5.tgz", + "integrity": "sha512-zj251pcPXI8GO9NDKWWmygP6+UjwWmrdf9qMW/L/uQJBM/0XbU2inxe5io/234y/RCvwpKEYjZ6c1YrXERkK4Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.30.5.tgz", + "integrity": "sha512-NJ6F+YHHFT/30isRe2UTmIGGAiXKckCyMnIV58cE3JkHmaD6e5zyEYm5hBDv0Wbin+IC0T1FWJpD3YqHUG/Ydg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.30.5.tgz", + "integrity": "sha512-k9+ejlv1GgwN1nN7XjVtyCgE0BTzhzT1YsQF0rv4Vfj2U9xnslBgMYYvcEYAFVdvhuEscELJsB7lDkN7WusErw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.30.5", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.30.5.tgz", + "integrity": "sha512-kZ80w/M2AvsbRvOr3PjaNh6qEW1LFqs2pLdo2s5R38B2HYXG8Z0PP48/4+j1QHJFL3ssHIbJ4odPRS8PlHrFfw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.30.5.tgz", + "integrity": "sha512-qGTc7QZC801kbYjAr4AgdOfnokpwStqyhSbiQvqGBLixniAKyH+ib2qXIVo4P9NgGzwyfD9I0nlJN7D91E1VpQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/visitor-keys": "5.30.5", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.30.5.tgz", + "integrity": "sha512-o4SSUH9IkuA7AYIfAvatldovurqTAHrfzPApOZvdUq01hHojZojCFXx06D/aFpKCgWbMPRdJBWAC3sWp3itwTA==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.30.5", + "@typescript-eslint/types": "5.30.5", + "@typescript-eslint/typescript-estree": "5.30.5", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.30.5", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.30.5.tgz", + "integrity": "sha512-D+xtGo9HUMELzWIUqcQc0p2PO4NyvTrgIOK/VnSH083+8sq0tiLozNRKuLarwHYGRuA6TVBQSuuLwJUDWd3aaA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.30.5", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.1.5.tgz", + "integrity": "sha512-O/ioqFpV+RvKbRykX2ItYPnbcZ4Hk5V0rY4uhQjQTLhGL9WZUvS7exzuYQCCI+ilSqJpctvxq2llTfGXf9UnnA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/azure-devops-node-api": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", + "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "dev": true, + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz", + "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "htmlparser2": "^8.0.1", + "parse5": "^7.0.0", + "parse5-htmlparser2-tree-adapter": "^7.0.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/d3": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", + "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", + "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush/node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", + "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag/node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-graphviz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/d3-graphviz/-/d3-graphviz-4.1.1.tgz", + "integrity": "sha512-s0IVbKf8rs4eJI2xo5Umr7nXDX/LEZw/x2WtKxmlyQxR0qUY49UiLhBNOX7VDHZywMle43NKEXnU6fn22fpJvQ==", + "dependencies": { + "@hpcc-js/wasm": "1.12.8", + "d3-dispatch": "^2.0.0", + "d3-format": "^2.0.0", + "d3-interpolate": "^2.0.1", + "d3-path": "^2.0.0", + "d3-timer": "^2.0.0", + "d3-transition": "^2.0.0", + "d3-zoom": "^2.0.0" + }, + "peerDependencies": { + "d3-selection": "^2.0.0" + } + }, + "node_modules/d3-graphviz/node_modules/d3-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-2.0.0.tgz", + "integrity": "sha512-SPXi0TSKPD4g9tw0NMZFnR95XVgUZiBH+uUTqQuDu1OsE2zomHU7ho0FISciaPvosimixwHFl3WHLGabv6dDgQ==" + }, + "node_modules/d3-graphviz/node_modules/d3-dispatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-2.0.0.tgz", + "integrity": "sha512-S/m2VsXI7gAti2pBoLClFFTMOO1HTtT0j99AuXLoGFKO6deHDdnv6ZGTxSTTUTgO1zVcv82fCOtDjYK4EECmWA==" + }, + "node_modules/d3-graphviz/node_modules/d3-drag": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-2.0.0.tgz", + "integrity": "sha512-g9y9WbMnF5uqB9qKqwIIa/921RYWzlUDv9Jl1/yONQwxbOfszAWTCm8u7HOTgJgRDXiRZN56cHT9pd24dmXs8w==", + "dependencies": { + "d3-dispatch": "1 - 2", + "d3-selection": "2" + } + }, + "node_modules/d3-graphviz/node_modules/d3-ease": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-2.0.0.tgz", + "integrity": "sha512-68/n9JWarxXkOWMshcT5IcjbB+agblQUaIsbnXmrzejn2O82n3p2A9R2zEB9HIEFWKFwPAEDDN8gR0VdSAyyAQ==" + }, + "node_modules/d3-graphviz/node_modules/d3-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-2.0.0.tgz", + "integrity": "sha512-Ab3S6XuE/Q+flY96HXT0jOXcM4EAClYFnRGY5zsjRGNy6qCYrQsMffs7cV5Q9xejb35zxW5hf/guKw34kvIKsA==" + }, + "node_modules/d3-graphviz/node_modules/d3-interpolate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-2.0.1.tgz", + "integrity": "sha512-c5UhwwTs/yybcmTpAVqwSFl6vrQ8JZJoT5F7xNFK9pymv5C0Ymcc9/LIJHtYIggg/yS9YHw8i8O8tgb9pupjeQ==", + "dependencies": { + "d3-color": "1 - 2" + } + }, + "node_modules/d3-graphviz/node_modules/d3-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-2.0.0.tgz", + "integrity": "sha512-ZwZQxKhBnv9yHaiWd6ZU4x5BtCQ7pXszEV9CU6kRgwIQVQGLMv1oiL4M+MK/n79sYzsj+gcgpPQSctJUsLN7fA==" + }, + "node_modules/d3-graphviz/node_modules/d3-timer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-2.0.0.tgz", + "integrity": "sha512-TO4VLh0/420Y/9dO3+f9abDEFYeCUr2WZRlxJvbp4HPTQcSylXNiL6yZa9FIUvV1yRiFufl1bszTCLDqv9PWNA==" + }, + "node_modules/d3-graphviz/node_modules/d3-transition": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-2.0.0.tgz", + "integrity": "sha512-42ltAGgJesfQE3u9LuuBHNbGrI/AJjNL2OAUdclE70UE6Vy239GCBEYD38uBPoLeNsOhFStGpPI0BAOV+HMxog==", + "dependencies": { + "d3-color": "1 - 2", + "d3-dispatch": "1 - 2", + "d3-ease": "1 - 2", + "d3-interpolate": "1 - 2", + "d3-timer": "1 - 2" + }, + "peerDependencies": { + "d3-selection": "2" + } + }, + "node_modules/d3-graphviz/node_modules/d3-zoom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-2.0.0.tgz", + "integrity": "sha512-fFg7aoaEm9/jf+qfstak0IYpnesZLiMX6GZvXtUSdv8RH2o4E2qeelgdU09eKS6wGuiGMfcnMI0nTIqWzRHGpw==", + "dependencies": { + "d3-dispatch": "1 - 2", + "d3-drag": "2", + "d3-interpolate": "1 - 2", + "d3-selection": "2", + "d3-transition": "2" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-2.0.0.tgz", + "integrity": "sha512-XoGGqhLUN/W14NmaqcO/bb1nqjDAw5WtSYb2X8wiuQWvSZUsUVYsOSkOybUrNvcBjaywBdYPy03eXHMXjk9nZA==" + }, + "node_modules/d3-shape": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3/node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.0.1.tgz", + "integrity": "sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.1" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.48.tgz", + "integrity": "sha512-w6N1Yn5MtqK2U1/WZTX9ZqUVb8IOLZkZ5AdHkT6x3cHDMVsYWC7WPdiLmx19w3i4Rwzy5LqsEMtVihG3e4rFzA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "esbuild-android-64": "0.14.48", + "esbuild-android-arm64": "0.14.48", + "esbuild-darwin-64": "0.14.48", + "esbuild-darwin-arm64": "0.14.48", + "esbuild-freebsd-64": "0.14.48", + "esbuild-freebsd-arm64": "0.14.48", + "esbuild-linux-32": "0.14.48", + "esbuild-linux-64": "0.14.48", + "esbuild-linux-arm": "0.14.48", + "esbuild-linux-arm64": "0.14.48", + "esbuild-linux-mips64le": "0.14.48", + "esbuild-linux-ppc64le": "0.14.48", + "esbuild-linux-riscv64": "0.14.48", + "esbuild-linux-s390x": "0.14.48", + "esbuild-netbsd-64": "0.14.48", + "esbuild-openbsd-64": "0.14.48", + "esbuild-sunos-64": "0.14.48", + "esbuild-windows-32": "0.14.48", + "esbuild-windows-64": "0.14.48", + "esbuild-windows-arm64": "0.14.48" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.48.tgz", + "integrity": "sha512-3aMjboap/kqwCUpGWIjsk20TtxVoKck8/4Tu19rubh7t5Ra0Yrpg30Mt1QXXlipOazrEceGeWurXKeFJgkPOUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.48.tgz", + "integrity": "sha512-vptI3K0wGALiDq+EvRuZotZrJqkYkN5282iAfcffjI5lmGG9G1ta/CIVauhY42MBXwEgDJkweiDcDMRLzBZC4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.48.tgz", + "integrity": "sha512-gGQZa4+hab2Va/Zww94YbshLuWteyKGD3+EsVon8EWTWhnHFRm5N9NbALNbwi/7hQ/hM1Zm4FuHg+k6BLsl5UA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.48.tgz", + "integrity": "sha512-bFjnNEXjhZT+IZ8RvRGNJthLWNHV5JkCtuOFOnjvo5pC0sk2/QVk0Qc06g2PV3J0TcU6kaPC3RN9yy9w2PSLEA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.48.tgz", + "integrity": "sha512-1NOlwRxmOsnPcWOGTB10JKAkYSb2nue0oM1AfHWunW/mv3wERfJmnYlGzL3UAOIUXZqW8GeA2mv+QGwq7DToqA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.48.tgz", + "integrity": "sha512-gXqKdO8wabVcYtluAbikDH2jhXp+Klq5oCD5qbVyUG6tFiGhrC9oczKq3vIrrtwcxDQqK6+HDYK8Zrd4bCA9Gw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.48.tgz", + "integrity": "sha512-ghGyDfS289z/LReZQUuuKq9KlTiTspxL8SITBFQFAFRA/IkIvDpnZnCAKTCjGXAmUqroMQfKJXMxyjJA69c/nQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.48.tgz", + "integrity": "sha512-vni3p/gppLMVZLghI7oMqbOZdGmLbbKR23XFARKnszCIBpEMEDxOMNIKPmMItQrmH/iJrL1z8Jt2nynY0bE1ug==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.48.tgz", + "integrity": "sha512-+VfSV7Akh1XUiDNXgqgY1cUP1i2vjI+BmlyXRfVz5AfV3jbpde8JTs5Q9sYgaoq5cWfuKfoZB/QkGOI+QcL1Tw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.48.tgz", + "integrity": "sha512-3CFsOlpoxlKPRevEHq8aAntgYGYkE1N9yRYAcPyng/p4Wyx0tPR5SBYsxLKcgPB9mR8chHEhtWYz6EZ+H199Zw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.48.tgz", + "integrity": "sha512-cs0uOiRlPp6ymknDnjajCgvDMSsLw5mST2UXh+ZIrXTj2Ifyf2aAP3Iw4DiqgnyYLV2O/v/yWBJx+WfmKEpNLA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.48.tgz", + "integrity": "sha512-+2F0vJMkuI0Wie/wcSPDCqXvSFEELH7Jubxb7mpWrA/4NpT+/byjxDz0gG6R1WJoeDefcrMfpBx4GFNN1JQorQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.48.tgz", + "integrity": "sha512-BmaK/GfEE+5F2/QDrIXteFGKnVHGxlnK9MjdVKMTfvtmudjY3k2t8NtlY4qemKSizc+QwyombGWTBDc76rxePA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.48.tgz", + "integrity": "sha512-tndw/0B9jiCL+KWKo0TSMaUm5UWBLsfCKVdbfMlb3d5LeV9WbijZ8Ordia8SAYv38VSJWOEt6eDCdOx8LqkC4g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.48.tgz", + "integrity": "sha512-V9hgXfwf/T901Lr1wkOfoevtyNkrxmMcRHyticybBUHookznipMOHoF41Al68QBsqBxnITCEpjjd4yAos7z9Tw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.48.tgz", + "integrity": "sha512-+IHf4JcbnnBl4T52egorXMatil/za0awqzg2Vy6FBgPcBpisDWT2sVz/tNdrK9kAqj+GZG/jZdrOkj7wsrNTKA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.48.tgz", + "integrity": "sha512-77m8bsr5wOpOWbGi9KSqDphcq6dFeJyun8TA+12JW/GAjyfTwVtOnN8DOt6DSPUfEV+ltVMNqtXUeTeMAxl5KA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.48.tgz", + "integrity": "sha512-EPgRuTPP8vK9maxpTGDe5lSoIBHGKO/AuxDncg5O3NkrPeLNdvvK8oywB0zGaAZXxYWfNNSHskvvDgmfVTguhg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.48.tgz", + "integrity": "sha512-YmpXjdT1q0b8ictSdGwH3M8VCoqPpK1/UArze3X199w6u8hUx3V8BhAi1WjbsfDYRBanVVtduAhh2sirImtAvA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.14.48", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.48.tgz", + "integrity": "sha512-HHaOMCsCXp0rz5BT2crTka6MPWVno121NKApsGs/OIW5QC0ggC69YMGs1aJct9/9FSUF4A1xNE/cLvgB5svR4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.19.0.tgz", + "integrity": "sha512-SXOPj3x9VKvPe81TjjUJCYlV4oJjQw68Uek+AM0X4p+33dj2HY5bpTZOgnQHcG2eAm1mtCU9uNMnJi7exU/kYw==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.0", + "@humanwhocodes/config-array": "^0.9.2", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.2", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-config-prettier": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", + "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.2.tgz", + "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==", + "dev": true, + "dependencies": { + "acorn": "^8.7.1", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.6.tgz", + "integrity": "sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ==", + "dev": true + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", + "dev": true + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.16.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.16.0.tgz", + "integrity": "sha512-A1lrQfpNF+McdPOnnFqY3kSN0AFTy485bTi1bkLk4mVPODIUEcSfhHgRqA+QdXPksrSTTztYXx37NFV+GpGk3Q==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.1.tgz", + "integrity": "sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "entities": "^4.3.0" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "dependencies": { + "ci-info": "^2.0.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/linkify-it": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", + "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "dev": true, + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "12.3.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", + "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "~2.1.0", + "linkify-it": "^3.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", + "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "dev": true, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.22.0.tgz", + "integrity": "sha512-u4uAs/4Zzmp/jjsD9cyFYDXeISfUWaAVWshPmDZOFOv4Xl4SbzTXm53I04C2uRueYJ+0t5PEtLH/owbn2Npf/w==", + "dev": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.12.2", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", + "integrity": "sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ovsx": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz", + "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==", + "dev": true, + "dependencies": { + "commander": "^6.1.0", + "follow-redirects": "^1.14.6", + "is-ci": "^2.0.0", + "leven": "^3.1.0", + "tmp": "^0.2.1", + "vsce": "^2.6.3" + }, + "bin": { + "ovsx": "lib/ovsx" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ovsx/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "dependencies": { + "entities": "^4.3.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz", + "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.2", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", + "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", + "dev": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dev": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tsutils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.9", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.9.tgz", + "integrity": "sha512-uSmjE38B80wjL85UFX3sTYEUlvZ1JgCRhsWj/fJ4rZ0FqDUFoIuodtiVeE+cUqiVTOKPdKrp/sdftD15MDek6g==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.4.tgz", + "integrity": "sha512-BQFnUDuAQ4Yf/cYY5LNrK9NCJFKriaRbD9uR1fTeXnBeoa97W0i41qkZfGO9pSo8I5KzjAcSY2XYtdf0oKd7KQ==", + "dev": true + }, + "node_modules/unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "node_modules/vsce": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/vsce/-/vsce-2.9.2.tgz", + "integrity": "sha512-xyLqL4U82BilUX1t6Ym2opQEa2tLGWYjbgB7+ETeNVXlIJz5sWBJjQJSYJVFOKJSpiOtQclolu88cj7oY6vvPQ==", + "dev": true, + "dependencies": { + "azure-devops-node-api": "^11.0.1", + "chalk": "^2.4.2", + "cheerio": "^1.0.0-rc.9", + "commander": "^6.1.0", + "glob": "^7.0.6", + "hosted-git-info": "^4.0.2", + "keytar": "^7.7.0", + "leven": "^3.1.0", + "markdown-it": "^12.3.2", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "semver": "^5.1.0", + "tmp": "^0.2.1", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.4.23", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/vsce/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/vsce/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/vsce/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/vsce/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/vsce/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/vsce/node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/vsce/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.0.0-next.7", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.0-next.7.tgz", + "integrity": "sha512-JX/F31LEsims0dAlOTKFE4E+AJMiJvdRSRViifFJSqSN7EzeYyWlfuDchF7g91oRNPZOIWfibTkDf3/UMsQGzQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "8.0.0-next.14", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.0-next.14.tgz", + "integrity": "sha512-NqjkOuDTMu8uo+PhoMsV72VO9Gd3wBi/ZpOrkRUOrWKQo7yUdiIw183g8wjH8BImgbK9ZP51HM7TI0ZhCnI1Mw==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.0-next.16" + }, + "engines": { + "vscode": "^1.66.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.0-next.16.tgz", + "integrity": "sha512-tx4DnXw9u3N7vw+bx6n2NKp6FoxoNwiP/biH83AS30I2AnTGyLd7afSeH6Oewn2E8jvB7K15bs12sMppkKOVeQ==", + "dependencies": { + "vscode-jsonrpc": "8.0.0-next.7", + "vscode-languageserver-types": "3.17.0-next.9" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.0-next.9", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.0-next.9.tgz", + "integrity": "sha512-9/PeDNPYduaoXRUzYpqmu4ZV9L01HGo0wH9FUt+sSHR7IXwA7xoXBfNUlv8gB9H0D2WwEmMomSy1NmhjKQyn3A==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/xml2js": { + "version": "0.4.23", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", + "integrity": "sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.0.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", + "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + } + }, "dependencies": { "@eslint/eslintrc": { "version": "1.3.0", @@ -213,7 +4161,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "agent-base": { "version": "6.0.2", @@ -1278,7 +5227,8 @@ "version": "8.5.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz", "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==", - "dev": true + "dev": true, + "requires": {} }, "eslint-scope": { "version": "5.1.1", @@ -2348,6 +6298,15 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, "string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -2358,15 +6317,6 @@ "strip-ansi": "^6.0.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 8a2dea6b35b7..27ab31db8db4 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -105,6 +105,22 @@ export async function createClient( traceOutputChannel: traceOutputChannel(), outputChannel: outputChannel(), middleware: { + async handleDiagnostics(uri, diagnostics, next) { + // Workaround for https://github.com/microsoft/vscode/issues/155531 + for (const diagnostic of diagnostics) { + if (!diagnostic.message) { + diagnostic.message = " "; + } + if (diagnostic.relatedInformation) { + for (const relatedInformation of diagnostic.relatedInformation) { + if (!relatedInformation.message) { + relatedInformation.message = " "; + } + } + } + } + next(uri, diagnostics); + }, async provideHover( document: vscode.TextDocument, position: vscode.Position, diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index bf55329ca1f5..1b793bb0b15c 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -326,8 +326,22 @@ export function toggleInlayHints(_ctx: Ctx): Cmd { const config = vscode.workspace.getConfiguration("editor.inlayHints", { languageId: "rust", }); - const value = !config.get("enabled"); - await config.update("enabled", value, vscode.ConfigurationTarget.Global); + + const value = config.get("enabled"); + let stringValue; + if (typeof value === "string") { + stringValue = value; + } else { + stringValue = value ? "on" : "off"; + } + const nextValues: Record = { + on: "off", + off: "on", + onUnlessPressed: "offUnlessPressed", + offUnlessPressed: "onUnlessPressed", + }; + const nextValue = nextValues[stringValue] ?? "on"; + await config.update("enabled", nextValue, vscode.ConfigurationTarget.Global); }; } diff --git a/lib/la-arena/src/lib.rs b/lib/la-arena/src/lib.rs index a3fe59e946ee..50e8d06b6604 100644 --- a/lib/la-arena/src/lib.rs +++ b/lib/la-arena/src/lib.rs @@ -6,7 +6,6 @@ use std::{ fmt, hash::{Hash, Hasher}, - iter::FromIterator, marker::PhantomData, ops::{Index, IndexMut, Range, RangeInclusive}, }; From 3a1aa376c55cd070474a8488a2cf30a600beeb2f Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Wed, 17 Aug 2022 04:58:26 +0900 Subject: [PATCH 06/22] avoid a `&str` to `String` conversion --- bench_data/glorious_old_parser | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench_data/glorious_old_parser b/bench_data/glorious_old_parser index 7e900dfeb1ee..764893daa12a 100644 --- a/bench_data/glorious_old_parser +++ b/bench_data/glorious_old_parser @@ -1988,7 +1988,7 @@ impl<'a> Parser<'a> { err.span_suggestion( span, "declare the type after the parameter binding", - String::from(": "), + ": ", Applicability::HasPlaceholders, ); } else if require_name && is_trait_item { From 31519bb394b609b12dc9a214284c8436e842c91c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 23 Aug 2022 10:05:52 +0300 Subject: [PATCH 07/22] :arrow_up: rust-analyzer --- .github/workflows/release.yaml | 6 +- Cargo.lock | 167 +++-- crates/flycheck/src/lib.rs | 28 +- crates/hir-def/src/body.rs | 5 + crates/hir-def/src/body/lower.rs | 10 +- crates/hir-def/src/body/pretty.rs | 621 ++++++++++++++++++ crates/hir-def/src/builtin_type.rs | 35 + crates/hir-def/src/expr.rs | 6 +- crates/hir-def/src/item_tree/pretty.rs | 179 +---- crates/hir-def/src/item_tree/tests.rs | 12 +- crates/hir-def/src/lib.rs | 1 + crates/hir-def/src/nameres/path_resolution.rs | 32 +- crates/hir-def/src/nameres/proc_macro.rs | 2 +- crates/hir-def/src/per_ns.rs | 12 + crates/hir-def/src/pretty.rs | 209 ++++++ crates/hir-def/src/type_ref.rs | 4 + crates/hir-expand/src/ast_id_map.rs | 21 +- crates/hir-expand/src/db.rs | 6 +- crates/hir-expand/src/lib.rs | 3 +- crates/hir-expand/src/mod_path.rs | 1 + crates/hir-expand/src/name.rs | 12 +- crates/hir-ty/Cargo.toml | 6 +- crates/hir-ty/src/infer.rs | 6 +- crates/hir-ty/src/lower.rs | 61 +- crates/hir-ty/src/method_resolution.rs | 142 ++-- crates/hir-ty/src/tests/method_resolution.rs | 43 ++ crates/hir-ty/src/tests/patterns.rs | 36 + crates/hir-ty/src/tests/regression.rs | 17 + crates/hir-ty/src/tests/traits.rs | 53 ++ crates/hir/src/lib.rs | 55 +- crates/hir/src/source_analyzer.rs | 32 +- .../src/handlers/extract_module.rs | 6 +- .../src/handlers/generate_function.rs | 142 +++- .../ide-assists/src/handlers/inline_call.rs | 35 +- .../src/handlers/inline_type_alias.rs | 229 ++++++- .../replace_turbofish_with_explicit_type.rs | 2 +- crates/ide-assists/src/lib.rs | 1 + crates/ide-assists/src/tests/generated.rs | 25 + crates/ide-assists/src/utils/suggest_name.rs | 5 +- crates/ide-completion/src/completions/dot.rs | 2 +- .../ide-completion/src/completions/keyword.rs | 70 +- .../src/completions/postfix/format_like.rs | 4 +- .../ide-completion/src/completions/record.rs | 10 +- crates/ide-completion/src/render.rs | 4 +- crates/ide-completion/src/render/literal.rs | 16 +- crates/ide-completion/src/render/pattern.rs | 18 +- .../src/render/union_literal.rs | 9 +- crates/ide-completion/src/render/variant.rs | 9 + crates/ide-completion/src/tests/flyimport.rs | 2 +- crates/ide-completion/src/tests/pattern.rs | 2 +- crates/ide-db/src/apply_change.rs | 2 +- crates/ide-db/src/rename.rs | 2 +- crates/ide-db/src/search.rs | 4 +- .../src/handlers/missing_match_arms.rs | 2 +- crates/ide/src/goto_definition.rs | 34 + crates/ide/src/hover.rs | 3 + crates/ide/src/hover/render.rs | 2 +- crates/ide/src/hover/tests.rs | 42 +- crates/ide/src/static_index.rs | 7 +- crates/ide/src/view_hir.rs | 14 +- crates/mbe/src/expander/matcher.rs | 2 +- crates/project-model/src/lib.rs | 2 +- crates/project-model/src/workspace.rs | 2 +- crates/rust-analyzer/src/bin/logger.rs | 2 +- crates/rust-analyzer/src/config.rs | 13 +- crates/rust-analyzer/src/global_state.rs | 15 +- crates/rust-analyzer/src/handlers.rs | 32 +- crates/rust-analyzer/src/lsp_ext.rs | 8 + crates/rust-analyzer/src/main_loop.rs | 7 +- crates/syntax/src/hacks.rs | 2 +- crates/test-utils/src/minicore.rs | 15 + crates/vfs-notify/Cargo.toml | 2 +- crates/vfs-notify/src/lib.rs | 11 +- crates/vfs/src/lib.rs | 2 +- docs/dev/architecture.md | 2 +- docs/dev/lsp-extensions.md | 2 +- docs/user/generated_config.adoc | 10 + editors/code/package.json | 12 +- editors/code/src/commands.ts | 10 +- editors/code/src/config.ts | 6 - editors/code/src/lsp_ext.ts | 30 +- editors/code/src/main.ts | 1 + editors/code/src/toolchain.ts | 2 +- 83 files changed, 2091 insertions(+), 625 deletions(-) create mode 100644 crates/hir-def/src/body/pretty.rs create mode 100644 crates/hir-def/src/pretty.rs diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3c36c4fb84a2..b2e5db6f689b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -31,13 +31,13 @@ jobs: - os: windows-latest target: aarch64-pc-windows-msvc code-target: win32-arm64 - - os: ubuntu-18.04 + - os: ubuntu-20.04 target: x86_64-unknown-linux-gnu code-target: linux-x64 - - os: ubuntu-18.04 + - os: ubuntu-20.04 target: aarch64-unknown-linux-gnu code-target: linux-arm64 - - os: ubuntu-18.04 + - os: ubuntu-20.04 target: arm-unknown-linux-gnueabihf code-target: linux-armhf - os: macos-11 diff --git a/Cargo.lock b/Cargo.lock index 7c6796d70bdc..8a61ea1c9241 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -37,9 +37,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.58" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704" +checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" [[package]] name = "anymap" @@ -78,16 +78,16 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.65" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61" +checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" dependencies = [ "addr2line", "cc", "cfg-if", "libc", "miniz_oxide", - "object 0.28.4", + "object", "rustc-demangle", ] @@ -114,9 +114,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "camino" -version = "1.0.9" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "869119e97797867fd90f5e22af7d0bd274bd4635ebb9eb68c04f3f513ae6c412" +checksum = "88ad0e1e3e88dd237a156ab9f571021b8a158caa0ae44b1968a241efb5144c1e" dependencies = [ "serde", ] @@ -171,9 +171,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chalk-derive" -version = "0.83.0" +version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83553c2ef7717e58aecdf42dd9e3c876229f5a1f35a16435b5ddc4addef81827" +checksum = "cf29c109d57f8d57b0e7675391be37a9285d86dd93278bd5f14a0ad3c447a6c2" dependencies = [ "proc-macro2", "quote", @@ -183,9 +183,9 @@ dependencies = [ [[package]] name = "chalk-ir" -version = "0.83.0" +version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dd42107d579d8ec2a5af20a8de62a37524a67bf6a4c0ff08a950068f0bfea91" +checksum = "d391763027b5e50a5e15caf6d2857ec585fd68160367bbeac9e1804209620918" dependencies = [ "bitflags", "chalk-derive", @@ -194,9 +194,9 @@ dependencies = [ [[package]] name = "chalk-recursive" -version = "0.83.0" +version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c444031541a76c13c145e76d91f1548e9feb2240e7f0c3e77879ceb694994f2d" +checksum = "afafd92dcdc7fe0ea940ee94bdd8cc5bd18f4a4a84c593d6d7025fe16c150478" dependencies = [ "chalk-derive", "chalk-ir", @@ -207,9 +207,9 @@ dependencies = [ [[package]] name = "chalk-solve" -version = "0.83.0" +version = "0.84.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c76f2db19c5e8a3d42340cf5b4d90b8c218750536fca35e2bb285ab6653c0bc8" +checksum = "3af1d111f11c91c48ace02e93e470c5bae6d2631bd112e4545317da53660d7fc" dependencies = [ "chalk-derive", "chalk-ir", @@ -249,9 +249,9 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" dependencies = [ "cfg-if", "crossbeam-channel", @@ -263,9 +263,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" dependencies = [ "cfg-if", "crossbeam-utils", @@ -273,9 +273,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" dependencies = [ "cfg-if", "crossbeam-epoch", @@ -284,9 +284,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" dependencies = [ "autocfg", "cfg-if", @@ -298,9 +298,9 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2" +checksum = "1cd42583b04998a5363558e5f9291ee5a5ff6b49944332103f251e7479a82aa7" dependencies = [ "cfg-if", "crossbeam-utils", @@ -308,9 +308,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" dependencies = [ "cfg-if", "once_cell", @@ -359,9 +359,9 @@ checksum = "9bda8e21c04aca2ae33ffc2fd8c23134f3cac46db123ba97bd9d3f3b8a4a85e1" [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "ena" @@ -458,15 +458,15 @@ checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a" [[package]] name = "gimli" -version = "0.26.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" +checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" [[package]] name = "hashbrown" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db0d4cf898abf0081f964436dc980e96670a0f36863e4b83aaacdb65c9d7ccc3" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "heck" @@ -794,9 +794,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" [[package]] name = "jod-thread" @@ -836,9 +836,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" [[package]] name = "libloading" @@ -944,9 +944,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.4" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5172b50c23043ff43dd53e51392f36519d9b35a8f3a410d30ece5d1aedd58ae" +checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" dependencies = [ "libc", ] @@ -1001,9 +1001,9 @@ dependencies = [ [[package]] name = "notify" -version = "5.0.0-pre.15" +version = "5.0.0-pre.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553f9844ad0b0824605c20fb55a661679782680410abfb1a8144c2e7e437e7a7" +checksum = "530f6314d6904508082f4ea424a0275cf62d341e118b313663f266429cb19693" dependencies = [ "bitflags", "crossbeam-channel", @@ -1027,15 +1027,6 @@ dependencies = [ "libc", ] -[[package]] -name = "object" -version = "0.28.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424" -dependencies = [ - "memchr", -] - [[package]] name = "object" version = "0.29.0" @@ -1047,9 +1038,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.13.0" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" [[package]] name = "oorandom" @@ -1118,9 +1109,9 @@ dependencies = [ [[package]] name = "paste" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c520e05135d6e763148b6426a837e239041653ba7becd2e538c076c738025fc" +checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22" [[package]] name = "paths" @@ -1172,7 +1163,7 @@ name = "proc-macro-api" version = "0.0.0" dependencies = [ "memmap2", - "object 0.29.0", + "object", "paths", "profile", "serde", @@ -1192,7 +1183,7 @@ dependencies = [ "libloading", "mbe", "memmap2", - "object 0.29.0", + "object", "paths", "proc-macro-api", "proc-macro-test", @@ -1221,9 +1212,9 @@ version = "0.0.0" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" dependencies = [ "unicode-ident", ] @@ -1265,9 +1256,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34f197a544b0c9ab3ae46c359a7ec9cbbb5c7bf97054266fecb7ead794a181d6" +checksum = "2d9cc634bc78768157b5cbfe988ffcd1dcba95cd2b2f03a88316c08c6d00ed63" dependencies = [ "bitflags", "memchr", @@ -1285,9 +1276,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.20" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" dependencies = [ "proc-macro2", ] @@ -1318,18 +1309,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ "bitflags", ] [[package]] name = "regex" -version = "1.5.6" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" +checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" dependencies = [ "regex-syntax", ] @@ -1345,9 +1336,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.26" +version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" +checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" [[package]] name = "rowan" @@ -1438,9 +1429,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "ryu" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" [[package]] name = "salsa" @@ -1494,27 +1485,27 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "semver" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2333e6df6d6598f2b1974829f853c2b4c5f4a6e503c10af918081aa6f8564e1" +checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" dependencies = [ "serde", ] [[package]] name = "serde" -version = "1.0.138" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1578c6245786b9d168c5447eeacfb96856573ca56c9d68fdcf394be134882a47" +checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.138" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "023e9b1467aef8a10fb88f25611870ada9800ef7e22afce356bb0d2387b6f27c" +checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" dependencies = [ "proc-macro2", "quote", @@ -1523,9 +1514,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ "indexmap", "itoa", @@ -1535,9 +1526,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2ad84e47328a31223de7fed7a4f5087f2d6ddfe586cf3ca25b7a165bc0a5aed" +checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" dependencies = [ "proc-macro2", "quote", @@ -1594,9 +1585,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" dependencies = [ "proc-macro2", "quote", @@ -1739,9 +1730,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" +checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" dependencies = [ "cfg-if", "pin-project-lite", @@ -1762,9 +1753,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b7358be39f2f274f322d2aaed611acc57f382e8eb1e5b48cb9ae30933495ce7" +checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" dependencies = [ "once_cell", "valuable", @@ -1783,9 +1774,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a713421342a5a666b7577783721d3117f1b69a393df803ee17bb73b1e122a59" +checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" dependencies = [ "matchers", "once_cell", diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 3347940ec6d6..c22945c81fcb 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -77,8 +77,13 @@ impl FlycheckHandle { } /// Schedule a re-start of the cargo check worker. - pub fn update(&self) { - self.sender.send(Restart).unwrap(); + pub fn restart(&self) { + self.sender.send(Restart::Yes).unwrap(); + } + + /// Stop this cargo check worker. + pub fn cancel(&self) { + self.sender.send(Restart::No).unwrap(); } pub fn id(&self) -> usize { @@ -122,7 +127,10 @@ pub enum Progress { DidCancel, } -struct Restart; +enum Restart { + Yes, + No, +} struct FlycheckActor { id: usize, @@ -149,6 +157,7 @@ impl FlycheckActor { config: FlycheckConfig, workspace_root: AbsPathBuf, ) -> FlycheckActor { + tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } } fn progress(&self, progress: Progress) { @@ -164,10 +173,13 @@ impl FlycheckActor { fn run(mut self, inbox: Receiver) { while let Some(event) = self.next_event(&inbox) { match event { - Event::Restart(Restart) => { + Event::Restart(Restart::No) => { + self.cancel_check_process(); + } + Event::Restart(Restart::Yes) => { // Cancel the previously spawned process self.cancel_check_process(); - while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} + while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {} let command = self.check_command(); tracing::debug!(?command, "will restart flycheck"); @@ -223,6 +235,10 @@ impl FlycheckActor { fn cancel_check_process(&mut self) { if let Some(cargo_handle) = self.cargo_handle.take() { + tracing::debug!( + command = ?self.check_command(), + "did cancel flycheck" + ); cargo_handle.cancel(); self.progress(Progress::DidCancel); } @@ -345,7 +361,7 @@ impl CargoActor { // // Because cargo only outputs one JSON object per line, we can // simply skip a line if it doesn't parse, which just ignores any - // erroneus output. + // erroneous output. let mut error = String::new(); let mut read_at_least_one_message = false; diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 080a307b1f8a..1d818d96267c 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -4,6 +4,7 @@ mod lower; #[cfg(test)] mod tests; pub mod scope; +mod pretty; use std::{ops::Index, sync::Arc}; @@ -352,6 +353,10 @@ impl Body { } } + pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String { + pretty::print_body_hir(db, self, owner) + } + fn new( db: &dyn DefDatabase, expander: Expander, diff --git a/crates/hir-def/src/body/lower.rs b/crates/hir-def/src/body/lower.rs index 66f9c24e8724..f6ec8bf7e9e0 100644 --- a/crates/hir-def/src/body/lower.rs +++ b/crates/hir-def/src/body/lower.rs @@ -551,9 +551,17 @@ impl ExprCollector<'_> { } } ast::Expr::MacroStmts(e) => { - let statements = e.statements().filter_map(|s| self.collect_stmt(s)).collect(); + let statements: Box<[_]> = + e.statements().filter_map(|s| self.collect_stmt(s)).collect(); let tail = e.expr().map(|e| self.collect_expr(e)); + if e.syntax().children().next().is_none() { + // HACK: make sure that macros that expand to nothing aren't treated as a `()` + // expression when used in block tail position. + cov_mark::hit!(empty_macro_in_trailing_position_is_removed); + return None; + } + self.alloc_expr(Expr::MacroStmts { tail, statements }, syntax_ptr) } ast::Expr::UnderscoreExpr(_) => self.alloc_expr(Expr::Underscore, syntax_ptr), diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs new file mode 100644 index 000000000000..ddd476efe5c4 --- /dev/null +++ b/crates/hir-def/src/body/pretty.rs @@ -0,0 +1,621 @@ +//! A pretty-printer for HIR. + +use std::fmt::{self, Write}; + +use crate::{ + expr::{Array, BindingAnnotation, Literal, Statement}, + pretty::{print_generic_args, print_path, print_type_ref}, + type_ref::TypeRef, +}; + +use super::*; + +pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { + let needs_semi; + let header = match owner { + DefWithBodyId::FunctionId(it) => { + needs_semi = false; + let item_tree_id = it.lookup(db).id; + format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::StaticId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::ConstId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { + Some(name) => name.to_string(), + None => "_".to_string(), + }; + format!("const {} = ", name) + } + }; + + let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false }; + p.print_expr(body.body_expr); + if needs_semi { + p.buf.push(';'); + } + p.buf +} + +macro_rules! w { + ($dst:expr, $($arg:tt)*) => { + { let _ = write!($dst, $($arg)*); } + }; +} + +macro_rules! wln { + ($dst:expr) => { + { let _ = writeln!($dst); } + }; + ($dst:expr, $($arg:tt)*) => { + { let _ = writeln!($dst, $($arg)*); } + }; +} + +struct Printer<'a> { + body: &'a Body, + buf: String, + indent_level: usize, + needs_indent: bool, +} + +impl<'a> Write for Printer<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for line in s.split_inclusive('\n') { + if self.needs_indent { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + 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'); + } + + Ok(()) + } +} + +impl<'a> Printer<'a> { + fn indented(&mut self, f: impl FnOnce(&mut Self)) { + self.indent_level += 1; + wln!(self); + f(self); + self.indent_level -= 1; + self.buf = self.buf.trim_end_matches('\n').to_string(); + } + + fn whitespace(&mut self) { + match self.buf.chars().next_back() { + None | Some('\n' | ' ') => {} + _ => self.buf.push(' '), + } + } + + fn newline(&mut self) { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + Some('\n') | None => {} + _ => writeln!(self).unwrap(), + } + } + + fn print_expr(&mut self, expr: ExprId) { + let expr = &self.body[expr]; + + match expr { + Expr::Missing => w!(self, "�"), + Expr::Underscore => w!(self, "_"), + Expr::Path(path) => self.print_path(path), + Expr::If { condition, then_branch, else_branch } => { + w!(self, "if "); + self.print_expr(*condition); + w!(self, " "); + self.print_expr(*then_branch); + if let Some(els) = *else_branch { + w!(self, " else "); + self.print_expr(els); + } + } + Expr::Let { pat, expr } => { + w!(self, "let "); + self.print_pat(*pat); + w!(self, " = "); + self.print_expr(*expr); + } + Expr::Loop { body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "loop "); + self.print_expr(*body); + } + Expr::While { condition, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "while "); + self.print_expr(*condition); + self.print_expr(*body); + } + Expr::For { iterable, pat, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "for "); + self.print_pat(*pat); + w!(self, " in "); + self.print_expr(*iterable); + self.print_expr(*body); + } + Expr::Call { callee, args, is_assignee_expr: _ } => { + self.print_expr(*callee); + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::MethodCall { receiver, method_name, args, generic_args } => { + self.print_expr(*receiver); + w!(self, ".{}", method_name); + if let Some(args) = generic_args { + w!(self, "::<"); + print_generic_args(args, self).unwrap(); + w!(self, ">"); + } + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::Match { expr, arms } => { + w!(self, "match "); + self.print_expr(*expr); + w!(self, " {{"); + self.indented(|p| { + for arm in &**arms { + p.print_pat(arm.pat); + if let Some(guard) = arm.guard { + w!(p, " if "); + p.print_expr(guard); + } + w!(p, " => "); + p.print_expr(arm.expr); + wln!(p, ","); + } + }); + wln!(self, "}}"); + } + Expr::Continue { label } => { + w!(self, "continue"); + if let Some(label) = label { + w!(self, " {}", label); + } + } + Expr::Break { expr, label } => { + w!(self, "break"); + if let Some(label) = label { + w!(self, " {}", label); + } + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Return { expr } => { + w!(self, "return"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Yield { expr } => { + w!(self, "yield"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, "{{"); + self.indented(|p| { + for field in &**fields { + w!(p, "{}: ", field.name); + p.print_expr(field.expr); + wln!(p, ","); + } + if let Some(spread) = spread { + w!(p, ".."); + p.print_expr(*spread); + wln!(p); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Expr::Field { expr, name } => { + self.print_expr(*expr); + w!(self, ".{}", name); + } + Expr::Await { expr } => { + self.print_expr(*expr); + w!(self, ".await"); + } + Expr::Try { expr } => { + self.print_expr(*expr); + w!(self, "?"); + } + Expr::TryBlock { body } => { + w!(self, "try "); + self.print_expr(*body); + } + Expr::Async { body } => { + w!(self, "async "); + self.print_expr(*body); + } + Expr::Const { body } => { + w!(self, "const "); + self.print_expr(*body); + } + Expr::Cast { expr, type_ref } => { + self.print_expr(*expr); + w!(self, " as "); + self.print_type_ref(type_ref); + } + Expr::Ref { expr, rawness, mutability } => { + w!(self, "&"); + if rawness.is_raw() { + w!(self, "raw "); + } + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_expr(*expr); + } + Expr::Box { expr } => { + w!(self, "box "); + self.print_expr(*expr); + } + Expr::UnaryOp { expr, op } => { + let op = match op { + ast::UnaryOp::Deref => "*", + ast::UnaryOp::Not => "!", + ast::UnaryOp::Neg => "-", + }; + w!(self, "{}", op); + self.print_expr(*expr); + } + Expr::BinaryOp { lhs, rhs, op } => { + let (bra, ket) = match op { + None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""), + _ => ("(", ")"), + }; + w!(self, "{}", bra); + self.print_expr(*lhs); + w!(self, "{} ", ket); + match op { + Some(op) => w!(self, "{}", op), + None => w!(self, "�"), // :) + } + w!(self, " {}", bra); + self.print_expr(*rhs); + w!(self, "{}", ket); + } + Expr::Range { lhs, rhs, range_type } => { + if let Some(lhs) = lhs { + w!(self, "("); + self.print_expr(*lhs); + w!(self, ") "); + } + let range = match range_type { + ast::RangeOp::Exclusive => "..", + ast::RangeOp::Inclusive => "..=", + }; + w!(self, "{}", range); + if let Some(rhs) = rhs { + w!(self, "("); + self.print_expr(*rhs); + w!(self, ") "); + } + } + Expr::Index { base, index } => { + self.print_expr(*base); + w!(self, "["); + self.print_expr(*index); + w!(self, "]"); + } + Expr::Closure { args, arg_types, ret_type, body } => { + w!(self, "|"); + for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_pat(*pat); + if let Some(ty) = ty { + w!(self, ": "); + self.print_type_ref(ty); + } + } + w!(self, "|"); + if let Some(ret_ty) = ret_type { + w!(self, " -> "); + self.print_type_ref(ret_ty); + } + self.whitespace(); + self.print_expr(*body); + } + Expr::Tuple { exprs, is_assignee_expr: _ } => { + w!(self, "("); + for expr in exprs.iter() { + self.print_expr(*expr); + w!(self, ", "); + } + w!(self, ")"); + } + Expr::Unsafe { body } => { + w!(self, "unsafe "); + self.print_expr(*body); + } + Expr::Array(arr) => { + w!(self, "["); + if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) { + self.indented(|p| match arr { + Array::ElementList { elements, is_assignee_expr: _ } => { + for elem in elements.iter() { + p.print_expr(*elem); + w!(p, ", "); + } + } + Array::Repeat { initializer, repeat } => { + p.print_expr(*initializer); + w!(p, "; "); + p.print_expr(*repeat); + } + }); + self.newline(); + } + w!(self, "]"); + } + Expr::Literal(lit) => self.print_literal(lit), + Expr::Block { id: _, statements, tail, label } => { + self.whitespace(); + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "{{"); + if !statements.is_empty() || tail.is_some() { + self.indented(|p| { + for stmt in &**statements { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + p.newline(); + }); + } + w!(self, "}}"); + } + Expr::MacroStmts { statements, tail } => { + w!(self, "{{ // macro statements"); + self.indented(|p| { + for stmt in statements.iter() { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + }); + self.newline(); + w!(self, "}}"); + } + } + } + + fn print_pat(&mut self, pat: PatId) { + let pat = &self.body[pat]; + + match pat { + Pat::Missing => w!(self, "�"), + Pat::Wild => w!(self, "_"), + Pat::Tuple { args, ellipsis } => { + w!(self, "("); + for (i, pat) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ".., "); + } + self.print_pat(*pat); + } + w!(self, ")"); + } + Pat::Or(pats) => { + for (i, pat) in pats.iter().enumerate() { + if i != 0 { + w!(self, " | "); + } + self.print_pat(*pat); + } + } + Pat::Record { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, " {{"); + self.indented(|p| { + for arg in args.iter() { + w!(p, "{}: ", arg.name); + p.print_pat(arg.pat); + wln!(p, ","); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Pat::Range { start, end } => { + self.print_expr(*start); + w!(self, "..."); + self.print_expr(*end); + } + Pat::Slice { prefix, slice, suffix } => { + w!(self, "["); + for pat in prefix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + if let Some(pat) = slice { + self.print_pat(*pat); + w!(self, ", "); + } + for pat in suffix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + w!(self, "]"); + } + Pat::Path(path) => self.print_path(path), + Pat::Lit(expr) => self.print_expr(*expr), + Pat::Bind { mode, name, subpat } => { + let mode = match mode { + BindingAnnotation::Unannotated => "", + BindingAnnotation::Mutable => "mut ", + BindingAnnotation::Ref => "ref ", + BindingAnnotation::RefMut => "ref mut ", + }; + w!(self, "{}{}", mode, name); + if let Some(pat) = subpat { + self.whitespace(); + self.print_pat(*pat); + } + } + Pat::TupleStruct { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + w!(self, "("); + for (i, arg) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ", .."); + } + self.print_pat(*arg); + } + w!(self, ")"); + } + Pat::Ref { pat, mutability } => { + w!(self, "&"); + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_pat(*pat); + } + Pat::Box { inner } => { + w!(self, "box "); + self.print_pat(*inner); + } + Pat::ConstBlock(c) => { + w!(self, "const "); + self.print_expr(*c); + } + } + } + + fn print_stmt(&mut self, stmt: &Statement) { + match stmt { + Statement::Let { pat, type_ref, initializer, else_branch } => { + w!(self, "let "); + self.print_pat(*pat); + if let Some(ty) = type_ref { + w!(self, ": "); + self.print_type_ref(ty); + } + if let Some(init) = initializer { + w!(self, " = "); + self.print_expr(*init); + } + if let Some(els) = else_branch { + w!(self, " else "); + self.print_expr(*els); + } + wln!(self, ";"); + } + Statement::Expr { expr, has_semi } => { + self.print_expr(*expr); + if *has_semi { + w!(self, ";"); + } + wln!(self); + } + } + } + + fn print_literal(&mut self, literal: &Literal) { + match literal { + Literal::String(it) => w!(self, "{:?}", it), + Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), + Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), + Literal::Bool(it) => w!(self, "{}", it), + Literal::Int(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Uint(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Float(f, suffix) => { + w!(self, "{}", f); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + } + } + + fn print_type_ref(&mut self, ty: &TypeRef) { + print_type_ref(ty, self).unwrap(); + } + + fn print_path(&mut self, path: &Path) { + print_path(path, self).unwrap(); + } +} diff --git a/crates/hir-def/src/builtin_type.rs b/crates/hir-def/src/builtin_type.rs index 25a408036ff7..dd69c3ab4731 100644 --- a/crates/hir-def/src/builtin_type.rs +++ b/crates/hir-def/src/builtin_type.rs @@ -156,3 +156,38 @@ impl BuiltinFloat { Some(res) } } + +impl fmt::Display for BuiltinInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinInt::Isize => "isize", + BuiltinInt::I8 => "i8", + BuiltinInt::I16 => "i16", + BuiltinInt::I32 => "i32", + BuiltinInt::I64 => "i64", + BuiltinInt::I128 => "i128", + }) + } +} + +impl fmt::Display for BuiltinUint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinUint::Usize => "usize", + BuiltinUint::U8 => "u8", + BuiltinUint::U16 => "u16", + BuiltinUint::U32 => "u32", + BuiltinUint::U64 => "u64", + BuiltinUint::U128 => "u128", + }) + } +} + +impl fmt::Display for BuiltinFloat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinFloat::F32 => "f32", + BuiltinFloat::F64 => "f64", + }) + } +} diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index c1b3788acb7d..4381b43c258b 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -12,6 +12,8 @@ //! //! See also a neighboring `body` module. +use std::fmt; + use hir_expand::name::Name; use la_arena::{Idx, RawIdx}; @@ -52,8 +54,8 @@ impl FloatTypeWrapper { } } -impl std::fmt::Display for FloatTypeWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for FloatTypeWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", f64::from_bits(self.0)) } } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index f12d9a1273cc..34dd817fd130 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -2,13 +2,10 @@ use std::fmt::{self, Write}; -use itertools::Itertools; - use crate::{ attr::RawAttrs, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, - path::GenericArg, - type_ref::TraitBoundModifier, + pretty::{print_path, print_type_bounds, print_type_ref}, visibility::RawVisibility, }; @@ -464,183 +461,15 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, type_ref: &TypeRef) { - // FIXME: deduplicate with `HirDisplay` impl - match type_ref { - TypeRef::Never => w!(self, "!"), - TypeRef::Placeholder => w!(self, "_"), - TypeRef::Tuple(fields) => { - w!(self, "("); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(field); - } - w!(self, ")"); - } - TypeRef::Path(path) => self.print_path(path), - TypeRef::RawPtr(pointee, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "*const", - Mutability::Mut => "*mut", - }; - w!(self, "{} ", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Reference(pointee, lt, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "", - Mutability::Mut => "mut ", - }; - w!(self, "&"); - if let Some(lt) = lt { - w!(self, "{} ", lt.name); - } - w!(self, "{}", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Array(elem, len) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "; {}]", len); - } - TypeRef::Slice(elem) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "]"); - } - TypeRef::Fn(args_and_ret, varargs) => { - let ((_, return_type), args) = - args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); - w!(self, "fn("); - for (i, (_, typeref)) in args.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(typeref); - } - if *varargs { - if !args.is_empty() { - w!(self, ", "); - } - w!(self, "..."); - } - w!(self, ") -> "); - self.print_type_ref(return_type); - } - TypeRef::Macro(_ast_id) => { - w!(self, ""); - } - TypeRef::Error => w!(self, "{{unknown}}"), - TypeRef::ImplTrait(bounds) => { - w!(self, "impl "); - self.print_type_bounds(bounds); - } - TypeRef::DynTrait(bounds) => { - w!(self, "dyn "); - self.print_type_bounds(bounds); - } - } + print_type_ref(type_ref, self).unwrap(); } fn print_type_bounds(&mut self, bounds: &[Interned]) { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - w!(self, " + "); - } - - match bound.as_ref() { - TypeBound::Path(path, modifier) => { - match modifier { - TraitBoundModifier::None => (), - TraitBoundModifier::Maybe => w!(self, "?"), - } - self.print_path(path) - } - TypeBound::ForLifetime(lifetimes, path) => { - w!(self, "for<{}> ", lifetimes.iter().format(", ")); - self.print_path(path); - } - TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), - TypeBound::Error => w!(self, "{{unknown}}"), - } - } + print_type_bounds(bounds, self).unwrap(); } fn print_path(&mut self, path: &Path) { - match path.type_anchor() { - Some(anchor) => { - w!(self, "<"); - self.print_type_ref(anchor); - w!(self, ">::"); - } - None => match path.kind() { - PathKind::Plain => {} - PathKind::Super(0) => w!(self, "self::"), - PathKind::Super(n) => { - for _ in 0..*n { - w!(self, "super::"); - } - } - PathKind::Crate => w!(self, "crate::"), - PathKind::Abs => w!(self, "::"), - PathKind::DollarCrate(_) => w!(self, "$crate::"), - }, - } - - for (i, segment) in path.segments().iter().enumerate() { - if i != 0 { - w!(self, "::"); - } - - w!(self, "{}", segment.name); - if let Some(generics) = segment.args_and_bindings { - // NB: these are all in type position, so `::<` turbofish syntax is not necessary - w!(self, "<"); - let mut first = true; - let args = if generics.has_self_type { - let (self_ty, args) = generics.args.split_first().unwrap(); - w!(self, "Self="); - self.print_generic_arg(self_ty); - first = false; - args - } else { - &generics.args - }; - for arg in args { - if !first { - w!(self, ", "); - } - first = false; - self.print_generic_arg(arg); - } - for binding in &generics.bindings { - if !first { - w!(self, ", "); - } - first = false; - w!(self, "{}", binding.name); - if !binding.bounds.is_empty() { - w!(self, ": "); - self.print_type_bounds(&binding.bounds); - } - if let Some(ty) = &binding.type_ref { - w!(self, " = "); - self.print_type_ref(ty); - } - } - - w!(self, ">"); - } - } - } - - fn print_generic_arg(&mut self, arg: &GenericArg) { - match arg { - GenericArg::Type(ty) => self.print_type_ref(ty), - GenericArg::Const(c) => w!(self, "{}", c), - GenericArg::Lifetime(lt) => w!(self, "{}", lt.name), - } + print_path(path, self).unwrap(); } fn print_generic_params(&mut self, params: &GenericParams) { diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 5cdf36cc61b8..e30d9652bb5d 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -283,10 +283,10 @@ struct S { "#, expect![[r#" pub(self) struct S { - pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>, - pub(self) b: Qualified::Syntax, - pub(self) c: ::Path<'a>, - pub(self) d: dyn for<'a> Trait<'a>, + pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>, + pub(self) b: Qualified::::Syntax, + pub(self) c: ::Path::<'a>, + pub(self) d: dyn for<'a> Trait::<'a>, } "#]], ) @@ -329,7 +329,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} T: Copy, U: ?Sized; - impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K> + impl<'a, 'b, T, const K: u8> S::<'a, 'b, T, K> where T: Copy, T: 'a, @@ -352,7 +352,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} where Self: Super, T: 'a, - Self: for<'a> Tr<'a, T> + Self: for<'a> Tr::<'a, T> { } "#]], diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 56603f4b1545..32ebfda4fd92 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -53,6 +53,7 @@ pub mod import_map; mod test_db; #[cfg(test)] mod macro_expansion_tests; +mod pretty; use std::{ hash::{Hash, Hasher}, diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index c579bc9194c3..8dfda6df64e7 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -399,14 +399,15 @@ impl DefMap { Some(_) | None => from_scope.or(from_builtin), }, }; - let from_extern_prelude = self - .extern_prelude - .get(name) - .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)); - let from_prelude = self.resolve_in_prelude(db, name); + let extern_prelude = || { + self.extern_prelude + .get(name) + .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) + }; + let prelude = || self.resolve_in_prelude(db, name); - from_legacy_macro.or(from_scope_or_builtin).or(from_extern_prelude).or(from_prelude) + from_legacy_macro.or(from_scope_or_builtin).or_else(extern_prelude).or_else(prelude) } fn resolve_name_in_crate_root_or_extern_prelude( @@ -414,20 +415,19 @@ impl DefMap { db: &dyn DefDatabase, name: &Name, ) -> PerNs { - let arc; - let crate_def_map = match self.block { + let from_crate_root = match self.block { Some(_) => { - arc = self.crate_root(db).def_map(db); - &arc + let def_map = self.crate_root(db).def_map(db); + def_map[def_map.root].scope.get(name) } - None => self, + None => self[self.root].scope.get(name), + }; + let from_extern_prelude = || { + self.resolve_name_in_extern_prelude(db, name) + .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) }; - let from_crate_root = crate_def_map[crate_def_map.root].scope.get(name); - let from_extern_prelude = self - .resolve_name_in_extern_prelude(db, name) - .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)); - from_crate_root.or(from_extern_prelude) + from_crate_root.or_else(from_extern_prelude) } fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs index 5089ef2d8171..52b79cd0fdda 100644 --- a/crates/hir-def/src/nameres/proc_macro.rs +++ b/crates/hir-def/src/nameres/proc_macro.rs @@ -45,7 +45,7 @@ impl Attrs { kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) }, }), - // `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]` + // `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]` [ TokenTree::Leaf(Leaf::Ident(trait_name)), TokenTree::Leaf(Leaf::Punct(comma)), diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs index bf5bf10c4caa..2bc1f8e926e9 100644 --- a/crates/hir-def/src/per_ns.rs +++ b/crates/hir-def/src/per_ns.rs @@ -43,6 +43,10 @@ impl PerNs { self.types.is_none() && self.values.is_none() && self.macros.is_none() } + pub fn is_full(&self) -> bool { + self.types.is_some() && self.values.is_some() && self.macros.is_some() + } + pub fn take_types(self) -> Option { self.types.map(|it| it.0) } @@ -84,6 +88,14 @@ impl PerNs { } } + pub fn or_else(self, f: impl FnOnce() -> PerNs) -> PerNs { + if self.is_full() { + self + } else { + self.or(f()) + } + } + pub fn iter_items(self) -> impl Iterator { let _p = profile::span("PerNs::iter_items"); self.types diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs new file mode 100644 index 000000000000..6636c8a23ca5 --- /dev/null +++ b/crates/hir-def/src/pretty.rs @@ -0,0 +1,209 @@ +//! Display and pretty printing routines. + +use std::fmt::{self, Write}; + +use hir_expand::mod_path::PathKind; +use itertools::Itertools; + +use crate::{ + intern::Interned, + path::{GenericArg, GenericArgs, Path}, + type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, +}; + +pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { + match path.type_anchor() { + Some(anchor) => { + write!(buf, "<")?; + print_type_ref(anchor, buf)?; + write!(buf, ">::")?; + } + None => match path.kind() { + PathKind::Plain => {} + PathKind::Super(0) => write!(buf, "self")?, + PathKind::Super(n) => { + for i in 0..*n { + if i == 0 { + buf.write_str("super")?; + } else { + buf.write_str("::super")?; + } + } + } + PathKind::Crate => write!(buf, "crate")?, + PathKind::Abs => {} + PathKind::DollarCrate(_) => write!(buf, "$crate")?, + }, + } + + for (i, segment) in path.segments().iter().enumerate() { + if i != 0 || !matches!(path.kind(), PathKind::Plain) { + write!(buf, "::")?; + } + + write!(buf, "{}", segment.name)?; + if let Some(generics) = segment.args_and_bindings { + write!(buf, "::<")?; + print_generic_args(generics, buf)?; + + write!(buf, ">")?; + } + } + + Ok(()) +} + +pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result { + let mut first = true; + let args = if generics.has_self_type { + let (self_ty, args) = generics.args.split_first().unwrap(); + write!(buf, "Self=")?; + print_generic_arg(self_ty, buf)?; + first = false; + args + } else { + &generics.args + }; + for arg in args { + if !first { + write!(buf, ", ")?; + } + first = false; + print_generic_arg(arg, buf)?; + } + for binding in &generics.bindings { + if !first { + write!(buf, ", ")?; + } + first = false; + write!(buf, "{}", binding.name)?; + if !binding.bounds.is_empty() { + write!(buf, ": ")?; + print_type_bounds(&binding.bounds, buf)?; + } + if let Some(ty) = &binding.type_ref { + write!(buf, " = ")?; + print_type_ref(ty, buf)?; + } + } + Ok(()) +} + +pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { + match arg { + GenericArg::Type(ty) => print_type_ref(ty, buf), + GenericArg::Const(c) => write!(buf, "{}", c), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), + } +} + +pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result { + // FIXME: deduplicate with `HirDisplay` impl + match type_ref { + TypeRef::Never => write!(buf, "!")?, + TypeRef::Placeholder => write!(buf, "_")?, + TypeRef::Tuple(fields) => { + write!(buf, "(")?; + for (i, field) in fields.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(field, buf)?; + } + write!(buf, ")")?; + } + TypeRef::Path(path) => print_path(path, buf)?, + TypeRef::RawPtr(pointee, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "*const", + Mutability::Mut => "*mut", + }; + write!(buf, "{} ", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Reference(pointee, lt, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "", + Mutability::Mut => "mut ", + }; + write!(buf, "&")?; + if let Some(lt) = lt { + write!(buf, "{} ", lt.name)?; + } + write!(buf, "{}", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Array(elem, len) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "; {}]", len)?; + } + TypeRef::Slice(elem) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "]")?; + } + TypeRef::Fn(args_and_ret, varargs) => { + let ((_, return_type), args) = + args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); + write!(buf, "fn(")?; + for (i, (_, typeref)) in args.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(typeref, buf)?; + } + if *varargs { + if !args.is_empty() { + write!(buf, ", ")?; + } + write!(buf, "...")?; + } + write!(buf, ") -> ")?; + print_type_ref(return_type, buf)?; + } + TypeRef::Macro(_ast_id) => { + write!(buf, "")?; + } + TypeRef::Error => write!(buf, "{{unknown}}")?, + TypeRef::ImplTrait(bounds) => { + write!(buf, "impl ")?; + print_type_bounds(bounds, buf)?; + } + TypeRef::DynTrait(bounds) => { + write!(buf, "dyn ")?; + print_type_bounds(bounds, buf)?; + } + } + + Ok(()) +} + +pub(crate) fn print_type_bounds( + bounds: &[Interned], + buf: &mut dyn Write, +) -> fmt::Result { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(buf, " + ")?; + } + + match bound.as_ref() { + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => write!(buf, "?")?, + } + print_path(path, buf)?; + } + TypeBound::ForLifetime(lifetimes, path) => { + write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; + print_path(path, buf)?; + } + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?, + TypeBound::Error => write!(buf, "{{unknown}}")?, + } + } + + Ok(()) +} diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs index 9248059627d2..5b4c71be7fb8 100644 --- a/crates/hir-def/src/type_ref.rs +++ b/crates/hir-def/src/type_ref.rs @@ -77,6 +77,10 @@ impl Rawness { Rawness::Ref } } + + pub fn is_raw(&self) -> bool { + matches!(self, Self::RawPtr) + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/crates/hir-expand/src/ast_id_map.rs b/crates/hir-expand/src/ast_id_map.rs index c1ddef03ba3b..11c0a6764e9d 100644 --- a/crates/hir-expand/src/ast_id_map.rs +++ b/crates/hir-expand/src/ast_id_map.rs @@ -15,7 +15,7 @@ use std::{ use la_arena::{Arena, Idx}; use profile::Count; use rustc_hash::FxHasher; -use syntax::{ast, match_ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; +use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr}; /// `AstId` points to an AST node in a specific file. pub struct FileAstId { @@ -92,18 +92,12 @@ impl AstIdMap { // change parent's id. This means that, say, adding a new function to a // trait does not change ids of top-level items, which helps caching. bdfs(node, |it| { - match_ast! { - match it { - ast::Item(module_item) => { - res.alloc(module_item.syntax()); - true - }, - ast::BlockExpr(block) => { - res.alloc(block.syntax()); - true - }, - _ => false, - } + let kind = it.kind(); + if ast::Item::can_cast(kind) || ast::BlockExpr::can_cast(kind) { + res.alloc(&it); + true + } else { + false } }); res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ()); @@ -123,6 +117,7 @@ impl AstIdMap { let raw = self.erased_ast_id(item.syntax()); FileAstId { raw, _ty: PhantomData } } + fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId { let ptr = SyntaxNodePtr::new(item); let hash = hash_ptr(&ptr); diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index bd60c3d26868..bc97ee15c7d3 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -321,7 +321,11 @@ fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet), BuiltIn(BuiltinFnLikeExpander, AstId), - // FIXME: maybe just Builtin and rename BuiltinFnLikeExpander to BuiltinExpander BuiltInAttr(BuiltinAttrExpander, AstId), BuiltInDerive(BuiltinDeriveExpander, AstId), BuiltInEager(EagerExpander, AstId), @@ -617,7 +616,7 @@ impl ExpansionInfo { let token_id = match token_id_in_attr_input { Some(token_id) => token_id, - // the token is not inside an attribute's input so do the lookup in the macro_arg as ususal + // the token is not inside an attribute's input so do the lookup in the macro_arg as usual None => { let relative_range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; diff --git a/crates/hir-expand/src/mod_path.rs b/crates/hir-expand/src/mod_path.rs index d0f73ec8208c..d7586d129b76 100644 --- a/crates/hir-expand/src/mod_path.rs +++ b/crates/hir-expand/src/mod_path.rs @@ -257,6 +257,7 @@ macro_rules! __known_path { (core::ops::RangeToInclusive) => {}; (core::ops::RangeInclusive) => {}; (core::future::Future) => {}; + (core::future::IntoFuture) => {}; (core::ops::Try) => {}; ($path:path) => { compile_error!("Please register your known path in the path module") diff --git a/crates/hir-expand/src/name.rs b/crates/hir-expand/src/name.rs index 87c663eec8e8..2b859f775095 100644 --- a/crates/hir-expand/src/name.rs +++ b/crates/hir-expand/src/name.rs @@ -90,10 +90,16 @@ impl Name { /// Resolve a name from the text of token. fn resolve(raw_text: &str) -> Name { - // When `raw_text` starts with "r#" but the name does not coincide with any - // keyword, we never need the prefix so we strip it. match raw_text.strip_prefix("r#") { + // When `raw_text` starts with "r#" but the name does not coincide with any + // keyword, we never need the prefix so we strip it. Some(text) if !is_raw_identifier(text) => Name::new_text(SmolStr::new(text)), + // Keywords (in the current edition) *can* be used as a name in earlier editions of + // Rust, e.g. "try" in Rust 2015. Even in such cases, we keep track of them in their + // escaped form. + None if is_raw_identifier(raw_text) => { + Name::new_text(SmolStr::from_iter(["r#", raw_text])) + } _ => Name::new_text(raw_text.into()), } } @@ -260,6 +266,7 @@ pub mod known { Try, Ok, Future, + IntoFuture, Result, Option, Output, @@ -393,6 +400,7 @@ pub mod known { future_trait, index, index_mut, + into_future, mul_assign, mul, neg, diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index 5cd444c1ad3b..7f143f396c76 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -18,9 +18,9 @@ ena = "0.14.0" tracing = "0.1.35" rustc-hash = "1.1.0" scoped-tls = "1.0.0" -chalk-solve = { version = "0.83.0", default-features = false } -chalk-ir = "0.83.0" -chalk-recursive = { version = "0.83.0", default-features = false } +chalk-solve = { version = "0.84.0", default-features = false } +chalk-ir = "0.84.0" +chalk-recursive = { version = "0.84.0", default-features = false } la-arena = { version = "0.3.0", path = "../../lib/la-arena" } once_cell = "1.12.0" typed-arena = "2.0.1" diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 46eeea0e6fc4..5df48e5fdcba 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -734,6 +734,7 @@ impl<'a> InferenceContext<'a> { let ty = self.insert_type_vars(ty.substitute(Interner, &substs)); return (ty, Some(strukt.into())); } + ValueNs::ImplSelf(impl_id) => (TypeNs::SelfType(impl_id), None), _ => return (self.err_ty(), None), }, Some(ResolveValueResult::Partial(typens, unresolved)) => (typens, Some(unresolved)), @@ -875,7 +876,10 @@ impl<'a> InferenceContext<'a> { } fn resolve_future_future_output(&self) -> Option { - let trait_ = self.resolve_lang_item(name![future_trait])?.as_trait()?; + let trait_ = self + .resolver + .resolve_known_trait(self.db.upcast(), &path![core::future::IntoFuture]) + .or_else(|| self.resolve_lang_item(name![future_trait])?.as_trait())?; self.db.trait_data(trait_).associated_type_by_name(&name![Output]) } diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 239f66bcb7e7..ae115c8c0da8 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -238,18 +238,7 @@ impl<'a> TyLoweringContext<'a> { }) .intern(Interner) } - TypeRef::DynTrait(bounds) => { - let self_ty = - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); - let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - QuantifiedWhereClauses::from_iter( - Interner, - bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), - ) - }); - let bounds = crate::make_single_type_binders(bounds); - TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) - } + TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode { ImplTraitLoweringMode::Opaque => { @@ -468,29 +457,10 @@ impl<'a> TyLoweringContext<'a> { } } 0 => { - let self_ty = Some( - TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)) - .intern(Interner), - ); - let trait_ref = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { - ctx.lower_trait_ref_from_resolved_path( - trait_, - resolved_segment, - self_ty, - ) - }); - let dyn_ty = DynTy { - bounds: crate::make_single_type_binders( - QuantifiedWhereClauses::from_iter( - Interner, - Some(crate::wrap_empty_binders(WhereClause::Implemented( - trait_ref, - ))), - ), - ), - lifetime: static_lifetime(), - }; - TyKind::Dyn(dyn_ty).intern(Interner) + // Trait object type without dyn; this should be handled in upstream. See + // `lower_path()`. + stdx::never!("unexpected fully resolved trait path"); + TyKind::Error.intern(Interner) } _ => { // FIXME report error (ambiguous associated type) @@ -555,11 +525,20 @@ impl<'a> TyLoweringContext<'a> { let (ty, res) = self.lower_ty_ext(type_ref); return self.lower_ty_relative_path(ty, res, path.segments()); } + let (resolution, remaining_index) = match self.resolver.resolve_path_in_type_ns(self.db.upcast(), path.mod_path()) { Some(it) => it, None => return (TyKind::Error.intern(Interner), None), }; + + if matches!(resolution, TypeNs::TraitId(_)) && remaining_index.is_none() { + // trait object type without dyn + let bound = TypeBound::Path(path.clone(), TraitBoundModifier::None); + let ty = self.lower_dyn_trait(&[Interned::new(bound)]); + return (ty, None); + } + let (resolved_segment, remaining_segments) = match remaining_index { None => ( path.segments().last().expect("resolved path has at least one element"), @@ -987,6 +966,18 @@ impl<'a> TyLoweringContext<'a> { }) } + fn lower_dyn_trait(&self, bounds: &[Interned]) -> Ty { + let self_ty = TyKind::BoundVar(BoundVar::new(DebruijnIndex::INNERMOST, 0)).intern(Interner); + let bounds = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { + QuantifiedWhereClauses::from_iter( + Interner, + bounds.iter().flat_map(|b| ctx.lower_type_bound(b, self_ty.clone(), false)), + ) + }); + let bounds = crate::make_single_type_binders(bounds); + TyKind::Dyn(DynTy { bounds, lifetime: static_lifetime() }).intern(Interner) + } + fn lower_impl_trait( &self, bounds: &[Interned], diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 64622545f840..9a63d5013b46 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -1064,6 +1064,14 @@ pub fn resolve_indexing_op( None } +macro_rules! check_that { + ($cond:expr) => { + if !$cond { + return false; + } + }; +} + fn is_valid_candidate( table: &mut InferenceTable<'_>, name: Option<&Name>, @@ -1072,54 +1080,10 @@ fn is_valid_candidate( self_ty: &Ty, visible_from_module: Option, ) -> bool { - macro_rules! check_that { - ($cond:expr) => { - if !$cond { - return false; - } - }; - } - let db = table.db; match item { AssocItemId::FunctionId(m) => { - let data = db.function_data(m); - - check_that!(name.map_or(true, |n| n == &data.name)); - check_that!(visible_from_module.map_or(true, |from_module| { - let v = db.function_visibility(m).is_visible_from(db.upcast(), from_module); - if !v { - cov_mark::hit!(autoderef_candidate_not_visible); - } - v - })); - - table.run_in_snapshot(|table| { - let subst = TyBuilder::subst_for_def(db, m).fill_with_inference_vars(table).build(); - let expect_self_ty = match m.lookup(db.upcast()).container { - ItemContainerId::TraitId(_) => { - subst.at(Interner, 0).assert_ty_ref(Interner).clone() - } - ItemContainerId::ImplId(impl_id) => { - subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) - } - // We should only get called for associated items (impl/trait) - ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { - unreachable!() - } - }; - check_that!(table.unify(&expect_self_ty, self_ty)); - if let Some(receiver_ty) = receiver_ty { - check_that!(data.has_self_param()); - - let sig = db.callable_item_signature(m.into()); - let expected_receiver = - sig.map(|s| s.params()[0].clone()).substitute(Interner, &subst); - - check_that!(table.unify(&receiver_ty, &expected_receiver)); - } - true - }) + is_valid_fn_candidate(table, m, name, receiver_ty, self_ty, visible_from_module) } AssocItemId::ConstId(c) => { let data = db.const_data(c); @@ -1152,6 +1116,94 @@ fn is_valid_candidate( } } +fn is_valid_fn_candidate( + table: &mut InferenceTable<'_>, + fn_id: FunctionId, + name: Option<&Name>, + receiver_ty: Option<&Ty>, + self_ty: &Ty, + visible_from_module: Option, +) -> bool { + let db = table.db; + let data = db.function_data(fn_id); + + check_that!(name.map_or(true, |n| n == &data.name)); + check_that!(visible_from_module.map_or(true, |from_module| { + let v = db.function_visibility(fn_id).is_visible_from(db.upcast(), from_module); + if !v { + cov_mark::hit!(autoderef_candidate_not_visible); + } + v + })); + + table.run_in_snapshot(|table| { + let container = fn_id.lookup(db.upcast()).container; + let impl_subst = match container { + ItemContainerId::ImplId(it) => { + TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + } + ItemContainerId::TraitId(it) => { + TyBuilder::subst_for_def(db, it).fill_with_inference_vars(table).build() + } + _ => unreachable!(), + }; + + let fn_subst = TyBuilder::subst_for_def(db, fn_id) + .use_parent_substs(&impl_subst) + .fill_with_inference_vars(table) + .build(); + + let expect_self_ty = match container { + ItemContainerId::TraitId(_) => fn_subst.at(Interner, 0).assert_ty_ref(Interner).clone(), + ItemContainerId::ImplId(impl_id) => { + fn_subst.apply(db.impl_self_ty(impl_id).skip_binders().clone(), Interner) + } + // We should only get called for associated items (impl/trait) + ItemContainerId::ModuleId(_) | ItemContainerId::ExternBlockId(_) => { + unreachable!() + } + }; + check_that!(table.unify(&expect_self_ty, self_ty)); + + if let Some(receiver_ty) = receiver_ty { + check_that!(data.has_self_param()); + + let sig = db.callable_item_signature(fn_id.into()); + let expected_receiver = + sig.map(|s| s.params()[0].clone()).substitute(Interner, &fn_subst); + + check_that!(table.unify(&receiver_ty, &expected_receiver)); + } + + if let ItemContainerId::ImplId(impl_id) = container { + // We need to consider the bounds on the impl to distinguish functions of the same name + // for a type. + let predicates = db.generic_predicates(impl_id.into()); + predicates + .iter() + .map(|predicate| { + let (p, b) = predicate + .clone() + .substitute(Interner, &impl_subst) + // Skipping the inner binders is ok, as we don't handle quantified where + // clauses yet. + .into_value_and_skipped_binders(); + stdx::always!(b.len(Interner) == 0); + p + }) + // It's ok to get ambiguity here, as we may not have enough information to prove + // obligations. We'll check if the user is calling the selected method properly + // later anyway. + .all(|p| table.try_obligation(p.cast(Interner)).is_some()) + } else { + // For `ItemContainerId::TraitId`, we check if `self_ty` implements the trait in + // `iterate_trait_method_candidates()`. + // For others, this function shouldn't be called. + true + } + }) +} + pub fn implements_trait( ty: &Canonical, db: &dyn HirDatabase, diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index 68463dc068bc..81588a7c4ffd 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -1790,3 +1790,46 @@ impl u16 { "#, ) } + +#[test] +fn with_impl_bounds() { + check_types( + r#" +trait Trait {} +struct Foo(T); +impl Trait for isize {} + +impl Foo { + fn foo() -> isize { 0 } + fn bar(&self) -> isize { 0 } +} + +impl Foo<()> { + fn foo() {} + fn bar(&self) {} +} + +fn f() { + let _ = Foo::::foo(); + //^isize + let _ = Foo(0isize).bar(); + //^isize + let _ = Foo::<()>::foo(); + //^() + let _ = Foo(()).bar(); + //^() + let _ = Foo::::foo(); + //^{unknown} + let _ = Foo(0usize).bar(); + //^{unknown} +} + +fn g(a: T) { + let _ = Foo::::foo(); + //^isize + let _ = Foo(a).bar(); + //^isize +} + "#, + ); +} diff --git a/crates/hir-ty/src/tests/patterns.rs b/crates/hir-ty/src/tests/patterns.rs index 94efe7bc11a8..eb04bf87783b 100644 --- a/crates/hir-ty/src/tests/patterns.rs +++ b/crates/hir-ty/src/tests/patterns.rs @@ -488,6 +488,42 @@ fn infer_adt_pattern() { ); } +#[test] +fn tuple_struct_destructured_with_self() { + check_infer( + r#" +struct Foo(usize,); +impl Foo { + fn f() { + let Self(s,) = &Foo(0,); + let Self(s,) = &mut Foo(0,); + let Self(s,) = Foo(0,); + } +} + "#, + expect![[r#" + 42..151 '{ ... }': () + 56..64 'Self(s,)': Foo + 61..62 's': &usize + 67..75 '&Foo(0,)': &Foo + 68..71 'Foo': Foo(usize) -> Foo + 68..75 'Foo(0,)': Foo + 72..73 '0': usize + 89..97 'Self(s,)': Foo + 94..95 's': &mut usize + 100..112 '&mut Foo(0,)': &mut Foo + 105..108 'Foo': Foo(usize) -> Foo + 105..112 'Foo(0,)': Foo + 109..110 '0': usize + 126..134 'Self(s,)': Foo + 131..132 's': usize + 137..140 'Foo': Foo(usize) -> Foo + 137..144 'Foo(0,)': Foo + 141..142 '0': usize + "#]], + ); +} + #[test] fn enum_variant_through_self_in_pattern() { check_infer( diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index 93a88ab58ef8..1b5ed0603bfd 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1648,3 +1648,20 @@ fn main() { "#]], ); } + +#[test] +fn trailing_empty_macro() { + cov_mark::check!(empty_macro_in_trailing_position_is_removed); + check_no_mismatches( + r#" +macro_rules! m2 { + ($($t:tt)*) => {$($t)*}; +} + +fn macrostmts() -> u8 { + m2! { 0 } + m2! {} +} + "#, + ); +} diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 75802a5eb4db..0f37970e2b38 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -137,6 +137,31 @@ fn not_send() -> Box + 'static> { ); } +#[test] +fn into_future_trait() { + check_types( + r#" +//- minicore: future +struct Futurable; +impl core::future::IntoFuture for Futurable { + type Output = u64; + type IntoFuture = IntFuture; +} + +struct IntFuture; +impl core::future::Future for IntFuture { + type Output = u64; +} + +fn test() { + let r = Futurable; + let v = r.await; + v; +} //^ u64 +"#, + ); +} + #[test] fn infer_try() { check_types( @@ -1476,6 +1501,34 @@ fn test(x: Trait, y: &Trait) -> u64 { 165..172 'z.foo()': u64 "#]], ); + + check_infer_with_mismatches( + r#" +//- minicore: fn, coerce_unsized +struct S; +impl S { + fn foo(&self) {} +} +fn f(_: &Fn(S)) {} +fn main() { + f(&|number| number.foo()); +} + "#, + expect![[r#" + 31..35 'self': &S + 37..39 '{}': () + 47..48 '_': &dyn Fn(S) + 58..60 '{}': () + 71..105 '{ ...()); }': () + 77..78 'f': fn f(&dyn Fn(S)) + 77..102 'f(&|nu...foo())': () + 79..101 '&|numb....foo()': &|S| -> () + 80..101 '|numbe....foo()': |S| -> () + 81..87 'number': S + 89..95 'number': S + 89..101 'number.foo()': () + "#]], + ) } #[test] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f984210e117..aa019ca48381 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -72,7 +72,7 @@ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use once_cell::unsync::Lazy; use rustc_hash::FxHashSet; -use stdx::{format_to, impl_from, never}; +use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, @@ -1136,6 +1136,20 @@ impl DefWithBody { } } + fn id(&self) -> DefWithBodyId { + match self { + DefWithBody::Function(it) => it.id.into(), + DefWithBody::Static(it) => it.id.into(), + DefWithBody::Const(it) => it.id.into(), + } + } + + /// A textual representation of the HIR of this def's body for debugging purposes. + pub fn debug_hir(self, db: &dyn HirDatabase) -> String { + let body = db.body(self.id()); + body.pretty_print(db.upcast(), self.id()) + } + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let krate = self.module(db).id.krate(); @@ -1470,19 +1484,6 @@ impl Function { let def_map = db.crate_def_map(loc.krate(db).into()); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - - /// A textual representation of the HIR of this function for debugging purposes. - pub fn debug_hir(self, db: &dyn HirDatabase) -> String { - let body = db.body(self.id.into()); - - let mut result = String::new(); - format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); - for (id, expr) in body.exprs.iter() { - format_to!(result, "{:?}: {:?}\n", id, expr); - } - - result - } } // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. @@ -2777,20 +2778,32 @@ impl Type { self.ty.is_unknown() } - /// Checks that particular type `ty` implements `std::future::Future`. + /// Checks that particular type `ty` implements `std::future::IntoFuture` or + /// `std::future::Future`. /// This function is used in `.await` syntax completion. - pub fn impls_future(&self, db: &dyn HirDatabase) -> bool { - let std_future_trait = db - .lang_item(self.env.krate, SmolStr::new_inline("future_trait")) - .and_then(|it| it.as_trait()); - let std_future_trait = match std_future_trait { + pub fn impls_into_future(&self, db: &dyn HirDatabase) -> bool { + let trait_ = db + .lang_item(self.env.krate, SmolStr::new_inline("into_future")) + .and_then(|it| { + let into_future_fn = it.as_function()?; + let assoc_item = as_assoc_item(db, AssocItem::Function, into_future_fn)?; + let into_future_trait = assoc_item.containing_trait_or_trait_impl(db)?; + Some(into_future_trait.id) + }) + .or_else(|| { + let future_trait = + db.lang_item(self.env.krate, SmolStr::new_inline("future_trait"))?; + future_trait.as_trait() + }); + + let trait_ = match trait_ { Some(it) => it, None => return false, }; let canonical_ty = Canonical { value: self.ty.clone(), binders: CanonicalVarKinds::empty(Interner) }; - method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), std_future_trait) + method_resolution::implements_trait(&canonical_ty, db, self.env.clone(), trait_) } /// Checks that particular type `ty` implements `std::ops::FnOnce`. diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index ae2896e19329..bd35af06e23e 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -27,6 +27,7 @@ use hir_def::{ use hir_expand::{ builtin_fn_macro::BuiltinFnLikeExpander, hygiene::Hygiene, + mod_path::path, name, name::{AsName, Name}, HirFileId, InFile, @@ -269,14 +270,35 @@ impl SourceAnalyzer { db: &dyn HirDatabase, await_expr: &ast::AwaitExpr, ) -> Option { - let ty = self.ty_of_expr(db, &await_expr.expr()?.into())?; + let mut ty = self.ty_of_expr(db, &await_expr.expr()?.into())?.clone(); + + let into_future_trait = self + .resolver + .resolve_known_trait(db.upcast(), &path![core::future::IntoFuture]) + .map(Trait::from); + + if let Some(into_future_trait) = into_future_trait { + let type_ = Type::new_with_resolver(db, &self.resolver, ty.clone()); + if type_.impls_trait(db, into_future_trait, &[]) { + let items = into_future_trait.items(db); + let into_future_type = items.into_iter().find_map(|item| match item { + AssocItem::TypeAlias(alias) + if alias.name(db) == hir_expand::name![IntoFuture] => + { + Some(alias) + } + _ => None, + })?; + let future_trait = type_.normalize_trait_assoc_type(db, &[], into_future_type)?; + ty = future_trait.ty; + } + } - let op_fn = db + let poll_fn = db .lang_item(self.resolver.krate(), hir_expand::name![poll].to_smol_str())? .as_function()?; - let substs = hir_ty::TyBuilder::subst_for_def(db, op_fn).push(ty.clone()).build(); - - Some(self.resolve_impl_method_or_trait_def(db, op_fn, &substs)) + let substs = hir_ty::TyBuilder::subst_for_def(db, poll_fn).push(ty.clone()).build(); + Some(self.resolve_impl_method_or_trait_def(db, poll_fn, &substs)) } pub(crate) fn resolve_prefix_expr( diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index b3c4d306ac1c..897980c66504 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -29,7 +29,7 @@ use super::remove_unused_param::range_to_remove; // Assist: extract_module // -// Extracts a selected region as seperate module. All the references, visibility and imports are +// Extracts a selected region as separate module. All the references, visibility and imports are // resolved. // // ``` @@ -105,7 +105,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // //- Thirdly, resolving all the imports this includes removing paths from imports // outside the module, shifting/cloning them inside new module, or shifting the imports, or making - // new import statemnts + // new import statements //We are getting item usages and record_fields together, record_fields //for change_visibility and usages for first point mentioned above in the process @@ -661,7 +661,7 @@ fn check_intersection_and_push( import_path: TextRange, ) { if import_paths_to_be_removed.len() > 0 { - // Text ranges recieved here for imports are extended to the + // Text ranges received here for imports are extended to the // next/previous comma which can cause intersections among them // and later deletion of these can cause panics similar // to reported in #11766. So to mitigate it, we diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index d564a0540898..e26c76da1891 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -61,56 +61,72 @@ fn gen_fn(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { } let fn_name = &*name_ref.text(); - let target_module; - let mut adt_name = None; + let TargetInfo { target_module, adt_name, target, file, insert_offset } = + fn_target_info(ctx, path, &call, fn_name)?; + let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; + let text_range = call.syntax().text_range(); + let label = format!("Generate {} function", function_builder.fn_name); + add_func_to_accumulator( + acc, + ctx, + text_range, + function_builder, + insert_offset, + file, + adt_name, + label, + ) +} + +struct TargetInfo { + target_module: Option, + adt_name: Option, + target: GeneratedFunctionTarget, + file: FileId, + insert_offset: TextSize, +} - let (target, file, insert_offset) = match path.qualifier() { +impl TargetInfo { + fn new( + target_module: Option, + adt_name: Option, + target: GeneratedFunctionTarget, + file: FileId, + insert_offset: TextSize, + ) -> Self { + Self { target_module, adt_name, target, file, insert_offset } + } +} + +fn fn_target_info( + ctx: &AssistContext<'_>, + path: ast::Path, + call: &CallExpr, + fn_name: &str, +) -> Option { + match path.qualifier() { Some(qualifier) => match ctx.sema.resolve_path(&qualifier) { Some(hir::PathResolution::Def(hir::ModuleDef::Module(module))) => { - target_module = Some(module); - get_fn_target(ctx, &target_module, call.clone())? + get_fn_target_info(ctx, &Some(module), call.clone()) } Some(hir::PathResolution::Def(hir::ModuleDef::Adt(adt))) => { if let hir::Adt::Enum(_) = adt { // Don't suggest generating function if the name starts with an uppercase letter - if name_ref.text().starts_with(char::is_uppercase) { + if fn_name.starts_with(char::is_uppercase) { return None; } } - let current_module = ctx.sema.scope(call.syntax())?.module(); - let module = adt.module(ctx.sema.db); - target_module = if current_module == module { None } else { Some(module) }; - if current_module.krate() != module.krate() { - return None; - } - let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; - let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; - adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; - (target, file, insert_offset) + assoc_fn_target_info(ctx, call, adt, fn_name) } - _ => { - return None; + Some(hir::PathResolution::SelfType(impl_)) => { + let adt = impl_.self_ty(ctx.db()).as_adt()?; + assoc_fn_target_info(ctx, call, adt, fn_name) } + _ => None, }, - _ => { - target_module = None; - get_fn_target(ctx, &target_module, call.clone())? - } - }; - let function_builder = FunctionBuilder::from_call(ctx, &call, fn_name, target_module, target)?; - let text_range = call.syntax().text_range(); - let label = format!("Generate {} function", function_builder.fn_name); - add_func_to_accumulator( - acc, - ctx, - text_range, - function_builder, - insert_offset, - file, - adt_name, - label, - ) + _ => get_fn_target_info(ctx, &None, call.clone()), + } } fn gen_method(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -366,6 +382,15 @@ fn make_return_type( (ret_type, should_focus_return_type) } +fn get_fn_target_info( + ctx: &AssistContext<'_>, + target_module: &Option, + call: CallExpr, +) -> Option { + let (target, file, insert_offset) = get_fn_target(ctx, target_module, call)?; + Some(TargetInfo::new(*target_module, None, target, file, insert_offset)) +} + fn get_fn_target( ctx: &AssistContext<'_>, target_module: &Option, @@ -399,6 +424,24 @@ fn get_method_target( Some((target.clone(), get_insert_offset(&target))) } +fn assoc_fn_target_info( + ctx: &AssistContext<'_>, + call: &CallExpr, + adt: hir::Adt, + fn_name: &str, +) -> Option { + let current_module = ctx.sema.scope(call.syntax())?.module(); + let module = adt.module(ctx.sema.db); + let target_module = if current_module == module { None } else { Some(module) }; + if current_module.krate() != module.krate() { + return None; + } + let (impl_, file) = get_adt_source(ctx, &adt, fn_name)?; + let (target, insert_offset) = get_method_target(ctx, &module, &impl_)?; + let adt_name = if impl_.is_none() { Some(adt.name(ctx.sema.db)) } else { None }; + Some(TargetInfo::new(target_module, adt_name, target, file, insert_offset)) +} + fn get_insert_offset(target: &GeneratedFunctionTarget) -> TextSize { match &target { GeneratedFunctionTarget::BehindItem(it) => it.text_range().end(), @@ -1633,6 +1676,33 @@ fn bar() ${0:-> _} { ) } + #[test] + fn create_static_method_within_an_impl_with_self_syntax() { + check_assist( + generate_function, + r" +struct S; +impl S { + fn foo(&self) { + Self::bar$0(); + } +} +", + r" +struct S; +impl S { + fn foo(&self) { + Self::bar(); + } + + fn bar() ${0:-> _} { + todo!() + } +} +", + ) + } + #[test] fn no_panic_on_invalid_global_path() { check_assist( diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 80d3b9255936..b5d092e39b02 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -13,7 +13,7 @@ use ide_db::{ use itertools::{izip, Itertools}; use syntax::{ ast::{self, edit_in_place::Indent, HasArgList, PathExpr}, - ted, AstNode, + ted, AstNode, NodeOrToken, SyntaxKind, }; use crate::{ @@ -311,6 +311,13 @@ fn inline( } else { fn_body.clone_for_update() }; + if let Some(t) = body.syntax().ancestors().find_map(ast::Impl::cast).and_then(|i| i.self_ty()) { + body.syntax() + .descendants_with_tokens() + .filter_map(NodeOrToken::into_token) + .filter(|tok| tok.kind() == SyntaxKind::SELF_TYPE_KW) + .for_each(|tok| ted::replace(tok, t.syntax())); + } let usages_for_locals = |local| { Definition::Local(local) .usages(sema) @@ -345,6 +352,7 @@ fn inline( } }) .collect(); + if function.self_param(sema.db).is_some() { let this = || make::name_ref("this").syntax().clone_for_update(); if let Some(self_local) = params[0].2.as_local(sema.db) { @@ -1188,6 +1196,31 @@ fn bar() -> u32 { x } } +"#, + ) + } + + #[test] + fn inline_call_with_self_type() { + check_assist( + inline_call, + r#" +struct A(u32); +impl A { + fn f() -> Self { Self(114514) } +} +fn main() { + A::f$0(); +} +"#, + r#" +struct A(u32); +impl A { + fn f() -> Self { Self(114514) } +} +fn main() { + A(114514); +} "#, ) } diff --git a/crates/ide-assists/src/handlers/inline_type_alias.rs b/crates/ide-assists/src/handlers/inline_type_alias.rs index 054663a06a11..9adf6381c1cb 100644 --- a/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -1,9 +1,9 @@ // Some ideas for future improvements: // - Support replacing aliases which are used in expressions, e.g. `A::new()`. -// - "inline_alias_to_users" assist #10881. // - Remove unused aliases if there are no longer any users, see inline_call.rs. use hir::{HasSource, PathResolution}; +use ide_db::{defs::Definition, search::FileReference}; use itertools::Itertools; use std::collections::HashMap; use syntax::{ @@ -16,6 +16,78 @@ use crate::{ AssistId, AssistKind, }; +// Assist: inline_type_alias_uses +// +// Inline a type alias into all of its uses where possible. +// +// ``` +// type $0A = i32; +// fn id(x: A) -> A { +// x +// }; +// fn foo() { +// let _: A = 3; +// } +// ``` +// -> +// ``` +// type A = i32; +// fn id(x: i32) -> i32 { +// x +// }; +// 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)?; + + let hir_alias = ctx.sema.to_def(&ast_alias)?; + let concrete_type = ast_alias.ty()?; + + let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema); + if !usages.at_least_one() { + return None; + } + + // until this is ok + + acc.add( + AssistId("inline_type_alias_uses", AssistKind::RefactorInline), + "Inline type alias into all uses", + name.syntax().text_range(), + |builder| { + let usages = usages.all(); + + let mut inline_refs_for_file = |file_id, refs: Vec| { + builder.edit_file(file_id); + + let path_types: Vec = refs + .into_iter() + .filter_map(|file_ref| match file_ref.name { + ast::NameLike::NameRef(path_type) => { + path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast) + } + _ => None, + }) + .collect(); + + for (target, replacement) in path_types.into_iter().filter_map(|path_type| { + let replacement = inline(&ast_alias, &path_type)?.to_text(&concrete_type); + let target = path_type.syntax().text_range(); + Some((target, replacement)) + }) { + builder.replace(target, replacement); + } + }; + + for (file_id, refs) in usages.into_iter() { + inline_refs_for_file(file_id, refs); + } + }, + ) +} + // Assist: inline_type_alias // // Replace a type alias with its concrete type. @@ -36,11 +108,6 @@ use crate::{ // } // ``` pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - enum Replacement { - Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap }, - Plain, - } - let alias_instance = ctx.find_node_at_offset::()?; let concrete_type; let replacement; @@ -59,23 +126,7 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O _ => { let alias = get_type_alias(&ctx, &alias_instance)?; concrete_type = alias.ty()?; - - replacement = if let Some(alias_generics) = alias.generic_param_list() { - if alias_generics.generic_params().next().is_none() { - cov_mark::hit!(no_generics_params); - return None; - } - - let instance_args = - alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast); - - Replacement::Generic { - lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?, - const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?, - } - } else { - Replacement::Plain - }; + replacement = inline(&alias, &alias_instance)?; } } @@ -85,19 +136,45 @@ pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> O AssistId("inline_type_alias", AssistKind::RefactorInline), "Inline type alias", target, - |builder| { - let replacement_text = match replacement { - Replacement::Generic { lifetime_map, const_and_type_map } => { - create_replacement(&lifetime_map, &const_and_type_map, &concrete_type) - } - Replacement::Plain => concrete_type.to_string(), - }; - - builder.replace(target, replacement_text); - }, + |builder| builder.replace(target, replacement.to_text(&concrete_type)), ) } +impl Replacement { + fn to_text(&self, concrete_type: &ast::Type) -> String { + match self { + Replacement::Generic { lifetime_map, const_and_type_map } => { + create_replacement(&lifetime_map, &const_and_type_map, &concrete_type) + } + Replacement::Plain => concrete_type.to_string(), + } + } +} + +enum Replacement { + Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap }, + Plain, +} + +fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option { + let repl = if let Some(alias_generics) = alias_def.generic_param_list() { + if alias_generics.generic_params().next().is_none() { + cov_mark::hit!(no_generics_params); + return None; + } + let instance_args = + alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast); + + Replacement::Generic { + lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?, + const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?, + } + } else { + Replacement::Plain + }; + Some(repl) +} + struct LifetimeMap(HashMap); impl LifetimeMap { @@ -835,4 +912,90 @@ trait Tr { "#, ); } + + mod inline_type_alias_uses { + use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist}; + + #[test] + fn inline_uses() { + check_assist( + inline_type_alias_uses, + r#" +type $0A = u32; + +fn foo() { + let _: A = 3; + let _: A = 4; +} +"#, + r#" +type A = u32; + +fn foo() { + let _: u32 = 3; + let _: u32 = 4; +} +"#, + ); + } + + #[test] + fn inline_uses_across_files() { + check_assist( + inline_type_alias_uses, + r#" +//- /lib.rs +mod foo; +type $0T = Vec; +fn f() -> T<&str> { + vec!["hello"] +} + +//- /foo.rs +use super::T; +fn foo() { + let _: T = Vec::new(); +} +"#, + r#" +//- /lib.rs +mod foo; +type T = Vec; +fn f() -> Vec<&str> { + vec!["hello"] +} + +//- /foo.rs +use super::T; +fn foo() { + let _: Vec = Vec::new(); +} +"#, + ); + } + + #[test] + fn inline_uses_across_files_2() { + check_assist( + inline_type_alias_uses, + r#" +//- /lib.rs +mod foo; +type $0I = i32; + +//- /foo.rs +use super::I; +fn foo() { + let _: I = 0; +} +"#, + r#" +use super::I; +fn foo() { + let _: i32 = 0; +} +"#, + ); + } + } } diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 6112e09455a4..5242f3b5100c 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -88,7 +88,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( }, ); } else if let Some(InferType(t)) = let_stmt.ty() { - // If there's a type inferrence underscore, we can offer to replace it with the type in + // If there's a type inference underscore, we can offer to replace it with the type in // the turbofish. // let x: _ = fn::<...>(); let underscore_range = t.syntax().text_range(); diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index fe87aa15fcf2..7fb35143fa2f 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -243,6 +243,7 @@ mod handlers { inline_call::inline_into_callers, inline_local_variable::inline_local_variable, inline_type_alias::inline_type_alias, + inline_type_alias::inline_type_alias_uses, introduce_named_generic::introduce_named_generic, introduce_named_lifetime::introduce_named_lifetime, invert_if::invert_if, diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 6eaab48a32ba..22319f36134f 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -1356,6 +1356,31 @@ fn main() { ) } +#[test] +fn doctest_inline_type_alias_uses() { + check_doc_test( + "inline_type_alias_uses", + r#####" +type $0A = i32; +fn id(x: A) -> A { + x +}; +fn foo() { + let _: A = 3; +} +"#####, + r#####" +type A = i32; +fn id(x: i32) -> i32 { + x +}; +fn foo() { + let _: i32 = 3; +} +"#####, + ) +} + #[test] fn doctest_introduce_named_generic() { check_doc_test( diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 779cdbc93c54..c521a10fccfc 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -55,6 +55,7 @@ const USELESS_METHODS: &[&str] = &[ "iter", "into_iter", "iter_mut", + "into_future", ]; pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { @@ -75,7 +76,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// In current implementation, the function tries to get the name from /// the following sources: /// -/// * if expr is an argument to function/method, use paramter name +/// * if expr is an argument to function/method, use parameter name /// * if expr is a function/method call, use function name /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) /// * fallback: `var_name` @@ -85,7 +86,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// 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 { - // `from_param` does not benifit from stripping + // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost if let Some(name) = from_param(expr, sema) { diff --git a/crates/ide-completion/src/completions/dot.rs b/crates/ide-completion/src/completions/dot.rs index cf40ca489c00..02004ff7b686 100644 --- a/crates/ide-completion/src/completions/dot.rs +++ b/crates/ide-completion/src/completions/dot.rs @@ -19,7 +19,7 @@ pub(crate) fn complete_dot( }; // Suggest .await syntax for types that implement Future trait - if receiver_ty.impls_future(ctx.db) { + if receiver_ty.impls_into_future(ctx.db) { let mut item = CompletionItem::new(CompletionItemKind::Keyword, ctx.source_range(), "await"); item.detail("expr.await"); diff --git a/crates/ide-completion/src/completions/keyword.rs b/crates/ide-completion/src/completions/keyword.rs index 3989a451bde4..1d03c8cc5ca6 100644 --- a/crates/ide-completion/src/completions/keyword.rs +++ b/crates/ide-completion/src/completions/keyword.rs @@ -75,16 +75,17 @@ impl Future for A {} fn foo(a: A) { a.$0 } "#, expect![[r#" - kw await expr.await - sn box Box::new(expr) - sn call function(expr) - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr + kw await expr.await + me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr "#]], ); @@ -98,18 +99,45 @@ fn foo() { } "#, expect![[r#" - kw await expr.await - sn box Box::new(expr) - sn call function(expr) - sn dbg dbg!(expr) - sn dbgr dbg!(&expr) - sn let let - sn letm let mut - sn match match expr {} - sn ref &expr - sn refm &mut expr + kw await expr.await + me into_future() (use core::future::IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr "#]], - ) + ); + } + + #[test] + fn test_completion_await_impls_into_future() { + check( + r#" +//- minicore: future +use core::future::*; +struct A {} +impl IntoFuture for A {} +fn foo(a: A) { a.$0 } +"#, + expect![[r#" + kw await expr.await + me into_future() (as IntoFuture) fn(self) -> ::IntoFuture + sn box Box::new(expr) + sn call function(expr) + sn dbg dbg!(expr) + sn dbgr dbg!(&expr) + sn let let + sn letm let mut + sn match match expr {} + sn ref &expr + sn refm &mut expr + "#]], + ); } #[test] diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index 6b94347e0ad0..b273a4cb53ba 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -173,7 +173,7 @@ impl FormatStrParser { } } (State::Expr, ':') if chars.peek().copied() == Some(':') => { - // path seperator + // path separator current_expr.push_str("::"); chars.next(); } @@ -185,7 +185,7 @@ impl FormatStrParser { current_expr = String::new(); self.state = State::FormatOpts; } else { - // We're inside of braced expression, assume that it's a struct field name/value delimeter. + // We're inside of braced expression, assume that it's a struct field name/value delimiter. current_expr.push(chr); } } diff --git a/crates/ide-completion/src/completions/record.rs b/crates/ide-completion/src/completions/record.rs index bfb98b9f2777..5d96fbd30a81 100644 --- a/crates/ide-completion/src/completions/record.rs +++ b/crates/ide-completion/src/completions/record.rs @@ -129,7 +129,7 @@ mod tests { #[test] fn literal_struct_completion_edit() { check_edit( - "FooDesc {…}", + "FooDesc{}", r#" struct FooDesc { pub bar: bool } @@ -154,7 +154,7 @@ fn baz() { #[test] fn literal_struct_impl_self_completion() { check_edit( - "Self {…}", + "Self{}", r#" struct Foo { bar: u64, @@ -180,7 +180,7 @@ impl Foo { ); check_edit( - "Self(…)", + "Self()", r#" mod submod { pub struct Foo(pub u64); @@ -209,7 +209,7 @@ impl submod::Foo { #[test] fn literal_struct_completion_from_sub_modules() { check_edit( - "submod::Struct {…}", + "submod::Struct{}", r#" mod submod { pub struct Struct { @@ -238,7 +238,7 @@ fn f() -> submod::Struct { #[test] fn literal_struct_complexion_module() { check_edit( - "FooDesc {…}", + "FooDesc{}", r#" mod _69latrick { pub struct FooDesc { pub six: bool, pub neuf: Vec, pub bar: bool } diff --git a/crates/ide-completion/src/render.rs b/crates/ide-completion/src/render.rs index 693007ca3071..a2cf6d68e5b3 100644 --- a/crates/ide-completion/src/render.rs +++ b/crates/ide-completion/src/render.rs @@ -565,6 +565,7 @@ fn main() { Foo::Fo$0 } kind: SymbolKind( Variant, ), + lookup: "Foo{}", detail: "Foo { x: i32, y: i32 }", }, ] @@ -591,6 +592,7 @@ fn main() { Foo::Fo$0 } kind: SymbolKind( Variant, ), + lookup: "Foo()", detail: "Foo(i32, i32)", }, ] @@ -707,7 +709,7 @@ fn main() { let _: m::Spam = S$0 } kind: SymbolKind( Variant, ), - lookup: "Spam::Bar(…)", + lookup: "Spam::Bar()", detail: "m::Spam::Bar(i32)", relevance: CompletionRelevance { exact_name_match: false, diff --git a/crates/ide-completion/src/render/literal.rs b/crates/ide-completion/src/render/literal.rs index 707dea206be5..af9c88a7e0a6 100644 --- a/crates/ide-completion/src/render/literal.rs +++ b/crates/ide-completion/src/render/literal.rs @@ -10,8 +10,8 @@ use crate::{ render::{ compute_ref_match, compute_type_match, variant::{ - format_literal_label, render_record_lit, render_tuple_lit, visible_fields, - RenderedLiteral, + format_literal_label, format_literal_lookup, render_record_lit, render_tuple_lit, + visible_fields, RenderedLiteral, }, RenderContext, }, @@ -97,13 +97,20 @@ fn render( if !should_add_parens { kind = StructKind::Unit; } + let label = format_literal_label(&qualified_name, kind); + let lookup = if qualified { + format_literal_lookup(&short_qualified_name.to_string(), kind) + } else { + format_literal_lookup(&qualified_name, kind) + }; let mut item = CompletionItem::new( CompletionItemKind::SymbolKind(thing.symbol_kind()), ctx.source_range(), - format_literal_label(&qualified_name, kind), + label, ); + item.lookup_by(lookup); item.detail(rendered.detail); match snippet_cap { @@ -111,9 +118,6 @@ fn render( None => item.insert_text(rendered.literal), }; - if qualified { - item.lookup_by(format_literal_label(&short_qualified_name.to_string(), kind)); - } item.set_documentation(thing.docs(db)).set_deprecated(thing.is_deprecated(&ctx)); let ty = thing.ty(db); diff --git a/crates/ide-completion/src/render/pattern.rs b/crates/ide-completion/src/render/pattern.rs index 1c1299e33b67..c845ff21aaba 100644 --- a/crates/ide-completion/src/render/pattern.rs +++ b/crates/ide-completion/src/render/pattern.rs @@ -8,7 +8,7 @@ use syntax::SmolStr; use crate::{ context::{ParamContext, ParamKind, PathCompletionCtx, PatternContext}, render::{ - variant::{format_literal_label, visible_fields}, + variant::{format_literal_label, format_literal_lookup, visible_fields}, RenderContext, }, CompletionItem, CompletionItemKind, @@ -34,9 +34,10 @@ pub(crate) fn render_struct_pat( let (name, escaped_name) = (name.unescaped().to_smol_str(), name.to_smol_str()); let kind = strukt.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); + let lookup = format_literal_lookup(name.as_str(), kind); let pat = render_pat(&ctx, pattern_ctx, &escaped_name, kind, &visible_fields, fields_omitted)?; - Some(build_completion(ctx, label, pat, strukt)) + Some(build_completion(ctx, label, lookup, pat, strukt)) } pub(crate) fn render_variant_pat( @@ -60,11 +61,14 @@ pub(crate) fn render_variant_pat( } }; - let (label, pat) = match path_ctx { - Some(PathCompletionCtx { has_call_parens: true, .. }) => (name, escaped_name.to_string()), + let (label, lookup, pat) = match path_ctx { + Some(PathCompletionCtx { has_call_parens: true, .. }) => { + (name.clone(), name, escaped_name.to_string()) + } _ => { let kind = variant.kind(ctx.db()); let label = format_literal_label(name.as_str(), kind); + let lookup = format_literal_lookup(name.as_str(), kind); let pat = render_pat( &ctx, pattern_ctx, @@ -73,16 +77,17 @@ pub(crate) fn render_variant_pat( &visible_fields, fields_omitted, )?; - (label, pat) + (label, lookup, pat) } }; - Some(build_completion(ctx, label, pat, variant)) + Some(build_completion(ctx, label, lookup, pat, variant)) } fn build_completion( ctx: RenderContext<'_>, label: SmolStr, + lookup: SmolStr, pat: String, def: impl HasAttrs + Copy, ) -> CompletionItem { @@ -90,6 +95,7 @@ fn build_completion( item.set_documentation(ctx.docs(def)) .set_deprecated(ctx.is_deprecated(def)) .detail(&pat) + .lookup_by(lookup) .set_relevance(ctx.completion_relevance()); match ctx.snippet_cap() { Some(snippet_cap) => item.insert_snippet(snippet_cap, pat), diff --git a/crates/ide-completion/src/render/union_literal.rs b/crates/ide-completion/src/render/union_literal.rs index bb32330f276d..54e97dd57ba8 100644 --- a/crates/ide-completion/src/render/union_literal.rs +++ b/crates/ide-completion/src/render/union_literal.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use crate::{ render::{ - variant::{format_literal_label, visible_fields}, + variant::{format_literal_label, format_literal_lookup, visible_fields}, RenderContext, }, CompletionItem, CompletionItemKind, @@ -24,13 +24,16 @@ pub(crate) fn render_union_literal( Some(p) => (p.unescaped().to_string(), p.to_string()), None => (name.unescaped().to_string(), name.to_string()), }; - + let label = format_literal_label(&name.to_smol_str(), StructKind::Record); + let lookup = format_literal_lookup(&name.to_smol_str(), StructKind::Record); let mut item = CompletionItem::new( CompletionItemKind::SymbolKind(SymbolKind::Union), ctx.source_range(), - format_literal_label(&name.to_smol_str(), StructKind::Record), + label, ); + item.lookup_by(lookup); + let fields = un.fields(ctx.db()); let (fields, fields_omitted) = visible_fields(ctx.completion, &fields, un)?; diff --git a/crates/ide-completion/src/render/variant.rs b/crates/ide-completion/src/render/variant.rs index 664845330eb8..24e6abdc9ad6 100644 --- a/crates/ide-completion/src/render/variant.rs +++ b/crates/ide-completion/src/render/variant.rs @@ -94,3 +94,12 @@ pub(crate) fn format_literal_label(name: &str, kind: StructKind) -> SmolStr { StructKind::Unit => name.into(), } } + +/// Format a struct, etc. literal option for lookup used in completions filtering. +pub(crate) fn format_literal_lookup(name: &str, kind: StructKind) -> SmolStr { + match kind { + StructKind::Tuple => SmolStr::from_iter([name, "()"]), + StructKind::Record => SmolStr::from_iter([name, "{}"]), + StructKind::Unit => name.into(), + } +} diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 0bba7f2459ca..a63ef006875b 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -159,7 +159,7 @@ pub mod some_module { pub struct ThiiiiiirdStruct; // contains all letters from the query, but not in the beginning, displayed second pub struct AfterThirdStruct; - // contains all letters from the query in the begginning, displayed first + // contains all letters from the query in the beginning, displayed first pub struct ThirdStruct; } diff --git a/crates/ide-completion/src/tests/pattern.rs b/crates/ide-completion/src/tests/pattern.rs index 30ddbe2dc6f6..85c4dbd6625d 100644 --- a/crates/ide-completion/src/tests/pattern.rs +++ b/crates/ide-completion/src/tests/pattern.rs @@ -467,7 +467,7 @@ fn foo() { fn completes_enum_variant_pat() { cov_mark::check!(enum_variant_pattern_path); check_edit( - "RecordVariant {…}", + "RecordVariant{}", r#" enum Enum { RecordVariant { field: u32 } diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs index f8134c552f75..b1ee9b58d5ba 100644 --- a/crates/ide-db/src/apply_change.rs +++ b/crates/ide-db/src/apply_change.rs @@ -20,7 +20,7 @@ impl RootDatabase { pub fn apply_change(&mut self, change: Change) { let _p = profile::span("RootDatabase::apply_change"); self.request_cancellation(); - tracing::info!("apply_change {:?}", change); + tracing::trace!("apply_change {:?}", change); if let Some(roots) = &change.roots { let mut local_roots = FxHashSet::default(); let mut library_roots = FxHashSet::default(); diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 517fe3f246d0..49b81265ea5b 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -82,7 +82,7 @@ impl Definition { } /// Textual range of the identifier which will change when renaming this - /// `Definition`. Note that some definitions, like buitin types, can't be + /// `Definition`. Note that some definitions, like builtin types, can't be /// renamed. pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option { let res = match self { diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 9eaabeec7a4e..2f4aa113170a 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -402,7 +402,9 @@ impl<'a> FindUsages<'a> { .or_else(|| ty.as_builtin().map(|builtin| builtin.name())) }) }; - self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.to_smol_str()) + // We need to unescape the name in case it is written without "r#" in earlier + // editions of Rust where it isn't a keyword. + self.def.name(sema.db).or_else(self_kw_refs).map(|it| it.unescaped().to_smol_str()) } }; let name = match &name { diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 9e66fbfb7522..5fcaf405b14b 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -750,7 +750,7 @@ fn main() { enum Foo { A } fn main() { // FIXME: this should not bail out but current behavior is such as the old algorithm. - // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. + // ExprValidator::validate_match(..) checks types of top level patterns incorrectly. match Foo::A { ref _x => {} Foo::A => {} diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index d6cd5783f05e..36a648fe4a8e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -1664,6 +1664,40 @@ fn f() { ); } + #[test] + fn goto_await_into_future_poll() { + check( + r#" +//- minicore: future + +struct Futurable; + +impl core::future::IntoFuture for Futurable { + type IntoFuture = MyFut; +} + +struct MyFut; + +impl core::future::Future for MyFut { + type Output = (); + + fn poll( + //^^^^ + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_> + ) -> std::task::Poll + { + () + } +} + +fn f() { + Futurable.await$0; +} +"#, + ); + } + #[test] fn goto_try_op() { check( diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 3ada181f1ed2..3687b597fc64 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -27,6 +27,7 @@ use crate::{ pub struct HoverConfig { pub links_in_hover: bool, pub documentation: Option, + pub keywords: bool, } impl HoverConfig { @@ -119,6 +120,8 @@ pub(crate) fn hover( } let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + // prefer descending the same token kind in attribute expansions, in normal macros text + // equivalency is more important let descended = if in_attr { [sema.descend_into_macros_with_kind_preference(original_token.clone())].into() } else { diff --git a/crates/ide/src/hover/render.rs b/crates/ide/src/hover/render.rs index 6c50a4e6adc0..d52adaee535f 100644 --- a/crates/ide/src/hover/render.rs +++ b/crates/ide/src/hover/render.rs @@ -230,7 +230,7 @@ pub(super) fn keyword( config: &HoverConfig, token: &SyntaxToken, ) -> Option { - if !token.kind().is_keyword() || !config.documentation.is_some() { + if !token.kind().is_keyword() || !config.documentation.is_some() || !config.keywords { return None; } let parent = token.parent()?; diff --git a/crates/ide/src/hover/tests.rs b/crates/ide/src/hover/tests.rs index c6274264b8f1..685eb4521ebd 100644 --- a/crates/ide/src/hover/tests.rs +++ b/crates/ide/src/hover/tests.rs @@ -8,7 +8,11 @@ fn check_hover_no_result(ra_fixture: &str) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap(); @@ -20,7 +24,11 @@ fn check(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -37,7 +45,11 @@ fn check_hover_no_links(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -54,7 +66,11 @@ fn check_hover_no_markdown(ra_fixture: &str, expect: Expect) { let (analysis, position) = fixture::position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::PlainText) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::PlainText), + keywords: true, + }, FileRange { file_id: position.file_id, range: TextRange::empty(position.offset) }, ) .unwrap() @@ -71,7 +87,11 @@ fn check_actions(ra_fixture: &str, expect: Expect) { let (analysis, file_id, position) = fixture::range_or_position(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, FileRange { file_id, range: position.range_or_empty() }, ) .unwrap() @@ -83,7 +103,11 @@ fn check_hover_range(ra_fixture: &str, expect: Expect) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap() @@ -95,7 +119,11 @@ fn check_hover_range_no_results(ra_fixture: &str) { let (analysis, range) = fixture::range(ra_fixture); let hover = analysis .hover( - &HoverConfig { links_in_hover: false, documentation: Some(HoverDocFormat::Markdown) }, + &HoverConfig { + links_in_hover: false, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }, range, ) .unwrap(); diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index cc79ee55b7da..9e5eb909508f 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -130,8 +130,11 @@ impl StaticIndex<'_> { syntax::NodeOrToken::Node(_) => None, syntax::NodeOrToken::Token(x) => Some(x), }); - let hover_config = - HoverConfig { links_in_hover: true, documentation: Some(HoverDocFormat::Markdown) }; + let hover_config = HoverConfig { + links_in_hover: true, + documentation: Some(HoverDocFormat::Markdown), + keywords: true, + }; let tokens = tokens.filter(|token| { matches!( token.kind(), diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs index bf0835ed7e0d..d2bbbf6d26ab 100644 --- a/crates/ide/src/view_hir.rs +++ b/crates/ide/src/view_hir.rs @@ -1,4 +1,4 @@ -use hir::{Function, Semantics}; +use hir::{DefWithBody, Semantics}; use ide_db::base_db::FilePosition; use ide_db::RootDatabase; use syntax::{algo::find_node_at_offset, ast, AstNode}; @@ -19,8 +19,12 @@ fn body_hir(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let source_file = sema.parse(position.file_id); - let function = find_node_at_offset::(source_file.syntax(), position.offset)?; - - let function: Function = sema.to_def(&function)?; - Some(function.debug_hir(db)) + let item = find_node_at_offset::(source_file.syntax(), position.offset)?; + let def: DefWithBody = match item { + ast::Item::Fn(it) => sema.to_def(&it)?.into(), + ast::Item::Const(it) => sema.to_def(&it)?.into(), + ast::Item::Static(it) => sema.to_def(&it)?.into(), + _ => return None, + }; + Some(def.debug_hir(db)) } diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 5020e9abaf31..c1aa14d6b7e7 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -321,7 +321,7 @@ struct MatchState<'t> { /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, - /// Number of tokens of seperator parsed + /// Number of tokens of separator parsed sep_parsed: Option, /// Matched meta variables bindings diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index e3f83084ac8a..b81b7432f655 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -3,7 +3,7 @@ //! //! Pure model is represented by the [`base_db::CrateGraph`] from another crate. //! -//! In this crate, we are conserned with "real world" project models. +//! In this crate, we are concerned with "real world" project models. //! //! Specifically, here we have a representation for a Cargo project //! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]). diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index daabb299f76c..8d6f50f5587c 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -770,7 +770,7 @@ fn handle_rustc_crates( queue.push_back(root_pkg); while let Some(pkg) = queue.pop_front() { // Don't duplicate packages if they are dependended on a diamond pattern - // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates + // N.B. if this line is omitted, we try to analyse over 4_800_000 crates // which is not ideal if rustc_pkg_crates.contains_key(&pkg) { continue; diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 0b69f75bc0db..298814af5a46 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs @@ -52,7 +52,7 @@ impl Logger { // merge chalk filter to our main filter (from RA_LOG env). // // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. - // As the value should only affect chalk crates, we'd better mannually + // As the value should only affect chalk crates, we'd better manually // specify the target. And for simplicity, CHALK_DEBUG only accept the value // that specify level. let chalk_level_dir = std::env::var("CHALK_DEBUG") diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1629c1dd328a..6649f42b4ef9 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -45,7 +45,8 @@ mod patch_old_style; // - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command // Defines the server-side configuration of the rust-analyzer. We generate -// *parts* of VS Code's `package.json` config from this. +// *parts* of VS Code's `package.json` config from this. Run `cargo test` to +// re-generate that file. // // However, editor specific config, which the server doesn't know about, should // be specified directly in `package.json`. @@ -120,6 +121,10 @@ config_data! { /// Cargo, you might also want to change /// `#rust-analyzer.cargo.buildScripts.overrideCommand#`. /// + /// If there are multiple linked projects, this command is invoked for + /// each of them, with the working directory being the project root + /// (i.e., the folder containing the `Cargo.toml`). + /// /// An example command would be: /// /// ```bash @@ -243,7 +248,10 @@ config_data! { hover_actions_run_enable: bool = "true", /// Whether to show documentation on hover. - hover_documentation_enable: bool = "true", + 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 in hover. hover_links_enable: bool = "true", @@ -1187,6 +1195,7 @@ impl Config { HoverDocFormat::PlainText } }), + keywords: self.data.hover_documentation_keywords_enable, } } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index b5f6aef2e1a8..c55bbbbe6ef7 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; -use ide_db::base_db::{CrateId, FileLoader, SourceDatabase, SourceDatabaseExt}; +use ide_db::base_db::{CrateId, FileLoader, SourceDatabase}; use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; @@ -176,9 +176,9 @@ impl GlobalState { pub(crate) fn process_changes(&mut self) -> bool { let _p = profile::span("GlobalState::process_changes"); - let mut fs_refresh_changes = Vec::new(); // A file was added or deleted let mut has_structure_changes = false; + let mut workspace_structure_change = None; let (change, changed_files) = { let mut change = Change::new(); @@ -192,7 +192,7 @@ impl GlobalState { if let Some(path) = vfs.file_path(file.file_id).as_path() { let path = path.to_path_buf(); if reload::should_refresh_for_change(&path, file.change_kind) { - fs_refresh_changes.push((path, file.file_id)); + workspace_structure_change = Some(path); } if file.is_created_or_deleted() { has_structure_changes = true; @@ -227,11 +227,10 @@ impl GlobalState { { let raw_database = self.analysis_host.raw_database(); - let workspace_structure_change = - fs_refresh_changes.into_iter().find(|&(_, file_id)| { - !raw_database.source_root(raw_database.file_source_root(file_id)).is_library - }); - if let Some((path, _)) = workspace_structure_change { + // FIXME: ideally we should only trigger a workspace fetch for non-library changes + // but somethings going wrong with the source root business when we add a new local + // crate see https://github.com/rust-lang/rust-analyzer/issues/13029 + if let Some(path) = workspace_structure_change { self.fetch_workspaces_queue .request_op(format!("workspace vfs file change: {}", path.display())); } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 943d043bc199..e0bcc80b31cb 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -51,6 +51,12 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { + let _p = profile::span("handle_stop_flycheck"); + state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); + Ok(()) +} + pub(crate) fn handle_analyzer_status( snap: GlobalStateSnapshot, params: lsp_ext::AnalyzerStatusParams, @@ -703,10 +709,8 @@ pub(crate) fn handle_runnables( let mut res = Vec::new(); for runnable in snap.analysis.runnables(file_id)? { - if let Some(offset) = offset { - if !runnable.nav.full_range.contains_inclusive(offset) { - continue; - } + if should_skip_for_offset(&runnable, offset) { + continue; } if should_skip_target(&runnable, cargo_spec.as_ref()) { continue; @@ -772,6 +776,14 @@ pub(crate) fn handle_runnables( Ok(res) } +fn should_skip_for_offset(runnable: &Runnable, offset: Option) -> bool { + match offset { + None => false, + _ if matches!(&runnable.kind, RunnableKind::TestMod { .. }) => false, + Some(offset) => !runnable.nav.full_range.contains_inclusive(offset), + } +} + pub(crate) fn handle_related_tests( snap: GlobalStateSnapshot, params: lsp_types::TextDocumentPositionParams, @@ -1765,7 +1777,7 @@ fn run_rustfmt( let line_index = snap.file_line_index(file_id)?; - let mut rustfmt = match snap.config.rustfmt() { + let mut command = match snap.config.rustfmt() { RustfmtConfig::Rustfmt { extra_args, enable_range_formatting } => { let mut cmd = process::Command::new(toolchain::rustfmt()); cmd.args(extra_args); @@ -1830,12 +1842,12 @@ fn run_rustfmt( } }; - let mut rustfmt = rustfmt + let mut rustfmt = command .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() - .context(format!("Failed to spawn {:?}", rustfmt))?; + .context(format!("Failed to spawn {:?}", command))?; rustfmt.stdin.as_mut().unwrap().write_all(file.as_bytes())?; @@ -1854,7 +1866,11 @@ fn run_rustfmt( // formatting because otherwise an error is surfaced to the user on top of the // syntax error diagnostics they're already receiving. This is especially jarring // if they have format on save enabled. - tracing::info!("rustfmt exited with status 1, assuming parse error and ignoring"); + tracing::warn!( + ?command, + %captured_stderr, + "rustfmt exited with status 1" + ); Ok(None) } _ => { diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 5f0e108624b2..e61c8b643d2d 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -129,6 +129,14 @@ pub struct ExpandedMacro { pub expansion: String, } +pub enum CancelFlycheck {} + +impl Request for CancelFlycheck { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/cancelFlycheck"; +} + pub enum MatchingBrace {} impl Request for MatchingBrace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 77419998249f..f187547019a1 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -288,7 +288,7 @@ impl GlobalState { if became_quiescent { // Project has loaded properly, kick off initial flycheck - self.flycheck.iter().for_each(FlycheckHandle::update); + self.flycheck.iter().for_each(FlycheckHandle::restart); if self.config.prefill_caches() { self.prime_caches_queue.request_op("became quiescent".to_string()); } @@ -590,6 +590,7 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) + .on_sync_mut::(handlers::handle_cancel_flycheck) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) @@ -779,7 +780,7 @@ impl GlobalState { for (id, _) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - flycheck.update(); + flycheck.restart(); continue; } } @@ -798,7 +799,7 @@ impl GlobalState { // No specific flycheck was triggered, so let's trigger all of them. if !updated { for flycheck in &this.flycheck { - flycheck.update(); + flycheck.restart(); } } Ok(()) diff --git a/crates/syntax/src/hacks.rs b/crates/syntax/src/hacks.rs index a047f61fa03c..ec3d3d444c36 100644 --- a/crates/syntax/src/hacks.rs +++ b/crates/syntax/src/hacks.rs @@ -1,4 +1,4 @@ -//! Things which exist to solve practial issues, but which shouldn't exist. +//! Things which exist to solve practical issues, but which shouldn't exist. //! //! Please avoid adding new usages of the functions in this module diff --git a/crates/test-utils/src/minicore.rs b/crates/test-utils/src/minicore.rs index f48d1ec66aae..6df29db4745d 100644 --- a/crates/test-utils/src/minicore.rs +++ b/crates/test-utils/src/minicore.rs @@ -471,6 +471,21 @@ pub mod future { #[lang = "poll"] fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll; } + + pub trait IntoFuture { + type Output; + type IntoFuture: Future; + #[lang = "into_future"] + fn into_future(self) -> Self::IntoFuture; + } + + impl IntoFuture for F { + type Output = F::Output; + type IntoFuture = F; + fn into_future(self) -> F { + self + } + } } pub mod task { pub enum Poll { diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index 9ee4415dcada..fcc693a7ddac 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -14,7 +14,7 @@ tracing = "0.1.35" jod-thread = "0.1.2" walkdir = "2.3.2" crossbeam-channel = "0.5.5" -notify = "=5.0.0-pre.15" +notify = "=5.0.0-pre.16" vfs = { path = "../vfs", version = "0.0.0" } paths = { path = "../paths", version = "0.0.0" } diff --git a/crates/vfs-notify/src/lib.rs b/crates/vfs-notify/src/lib.rs index d6d9c66159fe..c95304e55ac1 100644 --- a/crates/vfs-notify/src/lib.rs +++ b/crates/vfs-notify/src/lib.rs @@ -12,7 +12,7 @@ use std::fs; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; -use notify::{RecommendedWatcher, RecursiveMode, Watcher}; +use notify::{Config, RecommendedWatcher, RecursiveMode, Watcher}; use paths::{AbsPath, AbsPathBuf}; use vfs::loader; use walkdir::WalkDir; @@ -91,9 +91,12 @@ impl NotifyActor { self.watcher = None; if !config.watch.is_empty() { let (watcher_sender, watcher_receiver) = unbounded(); - let watcher = log_notify_error(RecommendedWatcher::new(move |event| { - watcher_sender.send(event).unwrap(); - })); + let watcher = log_notify_error(RecommendedWatcher::new( + move |event| { + watcher_sender.send(event).unwrap(); + }, + Config::default(), + )); self.watcher = watcher.map(|it| (it, watcher_receiver)); } diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 10fae41d081c..7badb1c363b4 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -64,7 +64,7 @@ pub struct FileId(pub u32); /// Storage for all files read by rust-analyzer. /// -/// For more informations see the [crate-level](crate) documentation. +/// For more information see the [crate-level](crate) documentation. #[derive(Default)] pub struct Vfs { interner: PathInterner, diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 51e26c58a917..c173a239feab 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -485,7 +485,7 @@ Mind the code--architecture gap: at the moment, we are using fewer feature flags ### Serialization In Rust, it is easy (often too easy) to add serialization to any type by adding `#[derive(Serialize)]`. -This easiness is misleading -- serializable types impose significant backwards compatability constraints. +This easiness is misleading -- serializable types impose significant backwards compatibility constraints. If a type is serializable, then it is a part of some IPC boundary. You often don't control the other side of this boundary, so changing serializable types is hard. diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 5040643d34a3..6d2c7d7b0634 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ diff --git a/.github/ISSUE_TEMPLATE/critical_nightly_regression.md b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md index a0b1627d7e2e..ad220ff65ca1 100644 --- a/.github/ISSUE_TEMPLATE/critical_nightly_regression.md +++ b/.github/ISSUE_TEMPLATE/critical_nightly_regression.md @@ -2,8 +2,8 @@ name: Critical Nightly Regression about: You are using nightly rust-analyzer and the latest version is unusable. title: '' -labels: '' -assignees: 'matklad' +labels: 'Broken Window' +assignees: '' --- @@ -14,4 +14,3 @@ Please try to provide information which will help us to fix the issue faster. Mi --> This is a serious regression in nightly and it's important to fix it before the next release. -@matklad, please take a look. diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 422fe29f9d5c..b070dd3406f2 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -257,8 +257,7 @@ jobs: - name: Publish Extension (OpenVSX, release) if: github.ref == 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') working-directory: ./editors/code - # token from https://dev.azure.com/rust-analyzer/ - run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true + run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 - name: Publish Extension (Code Marketplace, nightly) @@ -269,5 +268,5 @@ jobs: - name: Publish Extension (OpenVSX, nightly) if: github.ref != 'refs/heads/release' && (github.repository == 'rust-analyzer/rust-analyzer' || github.repository == 'rust-lang/rust-analyzer') working-directory: ./editors/code - run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix || true + run: npx ovsx publish --pat ${{ secrets.OPENVSX_TOKEN }} --packagePath ../../dist/rust-analyzer-*.vsix timeout-minutes: 2 diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs index 6636c8a23ca5..933970d10e47 100644 --- a/crates/hir-def/src/pretty.rs +++ b/crates/hir-def/src/pretty.rs @@ -143,9 +143,12 @@ pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Re print_type_ref(elem, buf)?; write!(buf, "]")?; } - TypeRef::Fn(args_and_ret, varargs) => { + TypeRef::Fn(args_and_ret, varargs, is_unsafe) => { let ((_, return_type), args) = args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); + if *is_unsafe { + write!(buf, "unsafe ")?; + } write!(buf, "fn(")?; for (i, (_, typeref)) in args.iter().enumerate() { if i != 0 { diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs index 5b4c71be7fb8..f8bb78ddcfe0 100644 --- a/crates/hir-def/src/type_ref.rs +++ b/crates/hir-def/src/type_ref.rs @@ -119,7 +119,7 @@ pub enum TypeRef { Array(Box, ConstScalarOrPath), Slice(Box), /// A fn pointer. Last element of the vector is the return type. - Fn(Vec<(Option, TypeRef)>, bool /*varargs*/), + Fn(Vec<(Option, TypeRef)>, bool /*varargs*/, bool /*is_unsafe*/), ImplTrait(Vec>), DynTrait(Vec>), Macro(AstId), @@ -229,7 +229,7 @@ impl TypeRef { Vec::new() }; params.push((None, ret_ty)); - TypeRef::Fn(params, is_varargs) + TypeRef::Fn(params, is_varargs, inner.unsafe_token().is_some()) } // for types are close enough for our purposes to the inner type for now... ast::Type::ForType(inner) => TypeRef::from_ast_opt(ctx, inner.ty()), @@ -263,7 +263,7 @@ impl TypeRef { fn go(type_ref: &TypeRef, f: &mut impl FnMut(&TypeRef)) { f(type_ref); match type_ref { - TypeRef::Fn(params, _) => { + TypeRef::Fn(params, _, _) => { params.iter().for_each(|(_, param_type)| go(param_type, f)) } TypeRef::Tuple(types) => types.iter().for_each(|t| go(t, f)), diff --git a/crates/hir-ty/src/display.rs b/crates/hir-ty/src/display.rs index 5ad661326353..a22a4b170f61 100644 --- a/crates/hir-ty/src/display.rs +++ b/crates/hir-ty/src/display.rs @@ -1187,8 +1187,11 @@ impl HirDisplay for TypeRef { inner.hir_fmt(f)?; write!(f, "]")?; } - TypeRef::Fn(parameters, is_varargs) => { + &TypeRef::Fn(ref parameters, is_varargs, is_unsafe) => { // FIXME: Function pointer qualifiers. + if is_unsafe { + write!(f, "unsafe ")?; + } write!(f, "fn(")?; if let Some(((_, return_type), function_parameters)) = parameters.split_last() { for index in 0..function_parameters.len() { @@ -1203,7 +1206,7 @@ impl HirDisplay for TypeRef { write!(f, ", ")?; } } - if *is_varargs { + if is_varargs { write!(f, "{}...", if parameters.len() == 1 { "" } else { ", " })?; } write!(f, ")")?; diff --git a/crates/hir-ty/src/infer.rs b/crates/hir-ty/src/infer.rs index 0efff651cc17..0b3c23f5747a 100644 --- a/crates/hir-ty/src/infer.rs +++ b/crates/hir-ty/src/infer.rs @@ -1020,7 +1020,7 @@ impl Expectation { /// The primary use case is where the expected type is a fat pointer, /// like `&[isize]`. For example, consider the following statement: /// - /// let x: &[isize] = &[1, 2, 3]; + /// let x: &[isize] = &[1, 2, 3]; /// /// In this case, the expected type for the `&[1, 2, 3]` expression is /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the diff --git a/crates/hir-ty/src/infer/expr.rs b/crates/hir-ty/src/infer/expr.rs index f56108b26c45..b1f4de826077 100644 --- a/crates/hir-ty/src/infer/expr.rs +++ b/crates/hir-ty/src/infer/expr.rs @@ -85,6 +85,7 @@ impl<'a> InferenceContext<'a> { let ty = match &self.body[tgt_expr] { Expr::Missing => self.err_ty(), &Expr::If { condition, then_branch, else_branch } => { + let expected = &expected.adjust_for_branches(&mut self.table); self.infer_expr( condition, &Expectation::has_type(TyKind::Scalar(Scalar::Bool).intern(Interner)), diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index 42c3b58d5ada..b68c764bdca0 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -38,10 +38,12 @@ use std::sync::Arc; use chalk_ir::{ fold::{Shift, TypeFoldable}, interner::HasInterner, - NoSolution, + NoSolution, UniverseIndex, }; use hir_def::{expr::ExprId, type_ref::Rawness, TypeOrConstParamId}; +use hir_expand::name; use itertools::Either; +use traits::FnTrait; use utils::Generics; use crate::{consteval::unknown_const, db::HirDatabase, utils::generics}; @@ -208,6 +210,7 @@ pub(crate) fn make_binders>( pub struct CallableSig { params_and_return: Arc<[Ty]>, is_varargs: bool, + safety: Safety, } has_interner!(CallableSig); @@ -216,9 +219,14 @@ has_interner!(CallableSig); pub type PolyFnSig = Binders; impl CallableSig { - pub fn from_params_and_return(mut params: Vec, ret: Ty, is_varargs: bool) -> CallableSig { + pub fn from_params_and_return( + mut params: Vec, + ret: Ty, + is_varargs: bool, + safety: Safety, + ) -> CallableSig { params.push(ret); - CallableSig { params_and_return: params.into(), is_varargs } + CallableSig { params_and_return: params.into(), is_varargs, safety } } pub fn from_fn_ptr(fn_ptr: &FnPointer) -> CallableSig { @@ -235,13 +243,14 @@ impl CallableSig { .map(|arg| arg.assert_ty_ref(Interner).clone()) .collect(), is_varargs: fn_ptr.sig.variadic, + safety: fn_ptr.sig.safety, } } pub fn to_fn_ptr(&self) -> FnPointer { FnPointer { num_binders: 0, - sig: FnSig { abi: (), safety: Safety::Safe, variadic: self.is_varargs }, + sig: FnSig { abi: (), safety: self.safety, variadic: self.is_varargs }, substitution: FnSubst(Substitution::from_iter( Interner, self.params_and_return.iter().cloned(), @@ -266,7 +275,11 @@ impl TypeFoldable for CallableSig { ) -> Result { let vec = self.params_and_return.to_vec(); let folded = vec.try_fold_with(folder, outer_binder)?; - Ok(CallableSig { params_and_return: folded.into(), is_varargs: self.is_varargs }) + Ok(CallableSig { + params_and_return: folded.into(), + is_varargs: self.is_varargs, + safety: self.safety, + }) } } @@ -508,3 +521,68 @@ where }); Canonical { value, binders: chalk_ir::CanonicalVarKinds::from_iter(Interner, kinds) } } + +pub fn callable_sig_from_fnonce( + self_ty: &Canonical, + env: Arc, + db: &dyn HirDatabase, +) -> Option { + let krate = env.krate; + let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; + let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; + + let mut kinds = self_ty.binders.interned().to_vec(); + let b = TyBuilder::trait_ref(db, fn_once_trait); + if b.remaining() != 2 { + return None; + } + let fn_once = b + .push(self_ty.value.clone()) + .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) + .build(); + kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| { + let vk = match x.data(Interner) { + chalk_ir::GenericArgData::Ty(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, + chalk_ir::GenericArgData::Const(c) => { + chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) + } + }; + chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + })); + + // FIXME: chalk refuses to solve `>::Output == ^0.1`, so we first solve + // `>` and then replace `^0.0` with the concrete argument tuple. + let trait_env = env.env.clone(); + let obligation = InEnvironment { goal: fn_once.cast(Interner), environment: trait_env }; + let canonical = + Canonical { binders: CanonicalVarKinds::from_iter(Interner, kinds), value: obligation }; + let subst = match db.trait_solve(krate, canonical) { + Some(Solution::Unique(vars)) => vars.value.subst, + _ => return None, + }; + let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?; + let params = match args.kind(Interner) { + chalk_ir::TyKind::Tuple(_, subst) => { + subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::>() + } + _ => return None, + }; + if params.iter().any(|ty| ty.is_unknown()) { + return None; + } + + let fn_once = TyBuilder::trait_ref(db, fn_once_trait) + .push(self_ty.value.clone()) + .push(args.clone()) + .build(); + let projection = + TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone())) + .build(); + + let ret_ty = db.normalize_projection(projection, env); + + Some(CallableSig::from_params_and_return(params, ret_ty.clone(), false, Safety::Safe)) +} diff --git a/crates/hir-ty/src/lower.rs b/crates/hir-ty/src/lower.rs index 22a85cf15458..baf9842d5fbf 100644 --- a/crates/hir-ty/src/lower.rs +++ b/crates/hir-ty/src/lower.rs @@ -227,13 +227,17 @@ impl<'a> TyLoweringContext<'a> { .intern(Interner) } TypeRef::Placeholder => TyKind::Error.intern(Interner), - TypeRef::Fn(params, is_varargs) => { + &TypeRef::Fn(ref params, variadic, is_unsafe) => { let substs = self.with_shifted_in(DebruijnIndex::ONE, |ctx| { Substitution::from_iter(Interner, params.iter().map(|(_, tr)| ctx.lower_ty(tr))) }); TyKind::Function(FnPointer { num_binders: 0, // FIXME lower `for<'a> fn()` correctly - sig: FnSig { abi: (), safety: Safety::Safe, variadic: *is_varargs }, + sig: FnSig { + abi: (), + safety: if is_unsafe { Safety::Unsafe } else { Safety::Safe }, + variadic, + }, substitution: FnSubst(substs), }) .intern(Interner) @@ -1573,7 +1577,12 @@ fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig { .with_type_param_mode(ParamLoweringMode::Variable); let ret = ctx_ret.lower_ty(&data.ret_type); let generics = generics(db.upcast(), def.into()); - let sig = CallableSig::from_params_and_return(params, ret, data.is_varargs()); + let sig = CallableSig::from_params_and_return( + params, + ret, + data.is_varargs(), + if data.has_unsafe_kw() { Safety::Unsafe } else { Safety::Safe }, + ); make_binders(db, &generics, sig) } @@ -1617,7 +1626,7 @@ fn fn_sig_for_struct_constructor(db: &dyn HirDatabase, def: StructId) -> PolyFnS TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>(); let (ret, binders) = type_for_adt(db, def.into()).into_value_and_skipped_binders(); - Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) + Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe)) } /// Build the type of a tuple struct constructor. @@ -1644,7 +1653,7 @@ fn fn_sig_for_enum_variant_constructor(db: &dyn HirDatabase, def: EnumVariantId) TyLoweringContext::new(db, &resolver).with_type_param_mode(ParamLoweringMode::Variable); let params = fields.iter().map(|(_, field)| ctx.lower_ty(&field.type_ref)).collect::>(); let (ret, binders) = type_for_adt(db, def.parent.into()).into_value_and_skipped_binders(); - Binders::new(binders, CallableSig::from_params_and_return(params, ret, false)) + Binders::new(binders, CallableSig::from_params_and_return(params, ret, false, Safety::Safe)) } /// Build the type of a tuple enum variant constructor. diff --git a/crates/hir-ty/src/tests/coercion.rs b/crates/hir-ty/src/tests/coercion.rs index d301595bcd98..7e3aecc2ae0a 100644 --- a/crates/hir-ty/src/tests/coercion.rs +++ b/crates/hir-ty/src/tests/coercion.rs @@ -122,6 +122,23 @@ fn test() { ) } +#[test] +fn if_else_adjust_for_branches_discard_type_var() { + check_no_mismatches( + r#" +fn test() { + let f = || { + if true { + &"" + } else { + "" + } + }; +} +"#, + ); +} + #[test] fn match_first_coerce() { check_no_mismatches( @@ -182,6 +199,22 @@ fn test() { ); } +#[test] +fn match_adjust_for_branches_discard_type_var() { + check_no_mismatches( + r#" +fn test() { + let f = || { + match 0i32 { + 0i32 => &"", + _ => "", + } + }; +} +"#, + ); +} + #[test] fn return_coerce_unknown() { check_types( @@ -357,7 +390,7 @@ fn test() { let f: fn(u32) -> isize = foo; // ^^^ adjustments: Pointer(ReifyFnPointer) let f: unsafe fn(u32) -> isize = foo; - // ^^^ adjustments: Pointer(ReifyFnPointer) + // ^^^ adjustments: Pointer(ReifyFnPointer), Pointer(UnsafeFnPointer) }", ); } @@ -388,7 +421,10 @@ fn coerce_closure_to_fn_ptr() { check_no_mismatches( r" fn test() { - let f: fn(u32) -> isize = |x| { 1 }; + let f: fn(u32) -> u32 = |x| x; + // ^^^^^ adjustments: Pointer(ClosureFnPointer(Safe)) + let f: unsafe fn(u32) -> u32 = |x| x; + // ^^^^^ adjustments: Pointer(ClosureFnPointer(Unsafe)) }", ); } diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f5324208c9a4..cbd9bf32a548 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2995,7 +2995,17 @@ impl Type { let callee = match self.ty.kind(Interner) { TyKind::Closure(id, _) => Callee::Closure(*id), TyKind::Function(_) => Callee::FnPtr, - _ => Callee::Def(self.ty.callable_def(db)?), + TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), + _ => { + let ty = hir_ty::replace_errors_with_variables(&self.ty); + let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?; + return Some(Callable { + ty: self.clone(), + sig, + callee: Callee::Other, + is_bound_method: false, + }); + } }; let sig = self.ty.callable_sig(db)?; @@ -3464,6 +3474,7 @@ enum Callee { Def(CallableDefId), Closure(ClosureId), FnPtr, + Other, } pub enum CallableKind { @@ -3472,6 +3483,8 @@ pub enum CallableKind { TupleEnumVariant(Variant), Closure, FnPtr, + /// Some other type that implements `FnOnce`. + Other, } impl Callable { @@ -3483,6 +3496,7 @@ impl Callable { Def(CallableDefId::EnumVariantId(it)) => CallableKind::TupleEnumVariant(it.into()), Closure(_) => CallableKind::Closure, FnPtr => CallableKind::FnPtr, + Other => CallableKind::Other, } } pub fn receiver_param(&self, db: &dyn HirDatabase) -> Option { diff --git a/crates/ide-assists/src/assist_config.rs b/crates/ide-assists/src/assist_config.rs index 60d1588a44e5..b273ebc85a50 100644 --- a/crates/ide-assists/src/assist_config.rs +++ b/crates/ide-assists/src/assist_config.rs @@ -14,4 +14,5 @@ pub struct AssistConfig { pub allowed: Option>, pub insert_use: InsertUseConfig, pub prefer_no_std: bool, + pub assist_emit_must_use: bool, } diff --git a/crates/ide-assists/src/handlers/add_explicit_type.rs b/crates/ide-assists/src/handlers/add_explicit_type.rs index bfa9759ec84b..b5f99726fe1c 100644 --- a/crates/ide-assists/src/handlers/add_explicit_type.rs +++ b/crates/ide-assists/src/handlers/add_explicit_type.rs @@ -69,14 +69,14 @@ pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> O let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?; acc.add( AssistId("add_explicit_type", AssistKind::RefactorRewrite), - format!("Insert explicit type `{}`", inferred_type), + format!("Insert explicit type `{inferred_type}`"), pat_range, |builder| match ascribed_ty { Some(ascribed_ty) => { builder.replace(ascribed_ty.syntax().text_range(), inferred_type); } None => { - builder.insert(pat_range.end(), format!(": {}", inferred_type)); + builder.insert(pat_range.end(), format!(": {inferred_type}")); } }, ) diff --git a/crates/ide-assists/src/handlers/add_return_type.rs b/crates/ide-assists/src/handlers/add_return_type.rs index f858d7a15c24..89040a8569e6 100644 --- a/crates/ide-assists/src/handlers/add_return_type.rs +++ b/crates/ide-assists/src/handlers/add_return_type.rs @@ -35,16 +35,16 @@ pub(crate) fn add_return_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt match builder_edit_pos { InsertOrReplace::Insert(insert_pos, needs_whitespace) => { let preceeding_whitespace = if needs_whitespace { " " } else { "" }; - builder.insert(insert_pos, &format!("{}-> {} ", preceeding_whitespace, ty)) + builder.insert(insert_pos, &format!("{preceeding_whitespace}-> {ty} ")) } InsertOrReplace::Replace(text_range) => { - builder.replace(text_range, &format!("-> {}", ty)) + builder.replace(text_range, &format!("-> {ty}")) } } if let FnType::Closure { wrap_expr: true } = fn_type { cov_mark::hit!(wrap_closure_non_block_expr); // `|x| x` becomes `|x| -> T x` which is invalid, so wrap it in a block - builder.replace(tail_expr.syntax().text_range(), &format!("{{{}}}", tail_expr)); + builder.replace(tail_expr.syntax().text_range(), &format!("{{{tail_expr}}}")); } }, ) diff --git a/crates/ide-assists/src/handlers/add_turbo_fish.rs b/crates/ide-assists/src/handlers/add_turbo_fish.rs index c0bf238db731..acf82e4b2579 100644 --- a/crates/ide-assists/src/handlers/add_turbo_fish.rs +++ b/crates/ide-assists/src/handlers/add_turbo_fish.rs @@ -93,12 +93,13 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.trigger_signature_help(); match ctx.config.snippet_cap { Some(cap) => { - let snip = format!("::<{}>", get_snippet_fish_head(number_of_arguments)); + let fish_head = get_snippet_fish_head(number_of_arguments); + let snip = format!("::<{fish_head}>"); builder.insert_snippet(cap, ident.text_range().end(), snip) } None => { let fish_head = std::iter::repeat("_").take(number_of_arguments).format(", "); - let snip = format!("::<{}>", fish_head); + let snip = format!("::<{fish_head}>"); builder.insert(ident.text_range().end(), snip); } } @@ -109,7 +110,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti /// This will create a snippet string with tabstops marked fn get_snippet_fish_head(number_of_arguments: usize) -> String { let mut fish_head = (1..number_of_arguments) - .format_with("", |i, f| f(&format_args!("${{{}:_}}, ", i))) + .format_with("", |i, f| f(&format_args!("${{{i}:_}}, "))) .to_string(); // tabstop 0 is a special case and always the last one diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs index 2853d1d1be3c..57cfa17cc8e1 100644 --- a/crates/ide-assists/src/handlers/apply_demorgan.rs +++ b/crates/ide-assists/src/handlers/apply_demorgan.rs @@ -123,20 +123,20 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let lhs_range = lhs.syntax().text_range(); let not_lhs = invert_boolean_expression(lhs); - edit.replace(lhs_range, format!("!({}", not_lhs.syntax().text())); + edit.replace(lhs_range, format!("!({not_lhs}")); } if let Some(rhs) = terms.pop_back() { let rhs_range = rhs.syntax().text_range(); let not_rhs = invert_boolean_expression(rhs); - edit.replace(rhs_range, format!("{})", not_rhs.syntax().text())); + edit.replace(rhs_range, format!("{not_rhs})")); } for term in terms { let term_range = term.syntax().text_range(); let not_term = invert_boolean_expression(term); - edit.replace(term_range, not_term.syntax().text()); + edit.replace(term_range, not_term.to_string()); } } }, diff --git a/crates/ide-assists/src/handlers/auto_import.rs b/crates/ide-assists/src/handlers/auto_import.rs index 678dc877d138..a689270bc091 100644 --- a/crates/ide-assists/src/handlers/auto_import.rs +++ b/crates/ide-assists/src/handlers/auto_import.rs @@ -127,10 +127,12 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< .sort_by_key(|import| Reverse(relevance_score(ctx, import, current_module.as_ref()))); for import in proposed_imports { + let import_path = import.import_path; + acc.add_group( &group_label, AssistId("auto_import", AssistKind::QuickFix), - format!("Import `{}`", import.import_path), + format!("Import `{import_path}`"), range, |builder| { let scope = match scope.clone() { @@ -138,7 +140,7 @@ pub(crate) fn auto_import(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< ImportScope::Module(it) => ImportScope::Module(builder.make_mut(it)), ImportScope::Block(it) => ImportScope::Block(builder.make_mut(it)), }; - insert_use(&scope, mod_path_to_ast(&import.import_path), &ctx.config.insert_use); + insert_use(&scope, mod_path_to_ast(&import_path), &ctx.config.insert_use); }, ); } diff --git a/crates/ide-assists/src/handlers/convert_comment_block.rs b/crates/ide-assists/src/handlers/convert_comment_block.rs index f171dd81a811..312cb65abd2a 100644 --- a/crates/ide-assists/src/handlers/convert_comment_block.rs +++ b/crates/ide-assists/src/handlers/convert_comment_block.rs @@ -54,16 +54,17 @@ fn block_to_line(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let indent_spaces = indentation.to_string(); let output = lines - .map(|l| l.trim_start_matches(&indent_spaces)) - .map(|l| { + .map(|line| { + let line = line.trim_start_matches(&indent_spaces); + // Don't introduce trailing whitespace - if l.is_empty() { + if line.is_empty() { line_prefix.to_string() } else { - format!("{} {}", line_prefix, l.trim_start_matches(&indent_spaces)) + format!("{line_prefix} {line}") } }) - .join(&format!("\n{}", indent_spaces)); + .join(&format!("\n{indent_spaces}")); edit.replace(target, output) }, @@ -96,7 +97,7 @@ fn line_to_block(acc: &mut Assists, comment: ast::Comment) -> Option<()> { let block_prefix = CommentKind { shape: CommentShape::Block, ..comment.kind() }.prefix(); - let output = format!("{}\n{}\n{}*/", block_prefix, block_comment_body, indentation); + let output = format!("{block_prefix}\n{block_comment_body}\n{indentation}*/"); edit.replace(target, output) }, diff --git a/crates/ide-assists/src/handlers/convert_integer_literal.rs b/crates/ide-assists/src/handlers/convert_integer_literal.rs index 9060696cdc8e..ff2195f7e6c4 100644 --- a/crates/ide-assists/src/handlers/convert_integer_literal.rs +++ b/crates/ide-assists/src/handlers/convert_integer_literal.rs @@ -32,19 +32,19 @@ pub(crate) fn convert_integer_literal(acc: &mut Assists, ctx: &AssistContext<'_> } let mut converted = match target_radix { - Radix::Binary => format!("0b{:b}", value), - Radix::Octal => format!("0o{:o}", value), + Radix::Binary => format!("0b{value:b}"), + Radix::Octal => format!("0o{value:o}"), Radix::Decimal => value.to_string(), - Radix::Hexadecimal => format!("0x{:X}", value), + Radix::Hexadecimal => format!("0x{value:X}"), }; - let label = format!("Convert {} to {}{}", literal, converted, suffix.unwrap_or_default()); - // Appends the type suffix back into the new literal if it exists. if let Some(suffix) = suffix { converted.push_str(suffix); } + let label = format!("Convert {literal} to {converted}"); + acc.add_group( &group_id, AssistId("convert_integer_literal", AssistKind::RefactorInline), diff --git a/crates/ide-assists/src/handlers/convert_into_to_from.rs b/crates/ide-assists/src/handlers/convert_into_to_from.rs index 95d11abe8bc0..872b52c98fff 100644 --- a/crates/ide-assists/src/handlers/convert_into_to_from.rs +++ b/crates/ide-assists/src/handlers/convert_into_to_from.rs @@ -86,9 +86,9 @@ pub(crate) fn convert_into_to_from(acc: &mut Assists, ctx: &AssistContext<'_>) - impl_.syntax().text_range(), |builder| { builder.replace(src_type.syntax().text_range(), dest_type.to_string()); - builder.replace(ast_trait.syntax().text_range(), format!("From<{}>", src_type)); + builder.replace(ast_trait.syntax().text_range(), format!("From<{src_type}>")); builder.replace(into_fn_return.syntax().text_range(), "-> Self"); - builder.replace(into_fn_params.syntax().text_range(), format!("(val: {})", src_type)); + builder.replace(into_fn_params.syntax().text_range(), format!("(val: {src_type})")); builder.replace(into_fn_name.syntax().text_range(), "from"); for s in selfs { diff --git a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs index 2cf370c09074..80eecf4a0986 100644 --- a/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs +++ b/crates/ide-assists/src/handlers/convert_iter_for_each_to_for.rs @@ -119,19 +119,19 @@ pub(crate) fn convert_for_loop_with_for_each( { // We have either "for x in &col" and col implements a method called iter // or "for x in &mut col" and col implements a method called iter_mut - format_to!(buf, "{}.{}()", expr_behind_ref, method); + format_to!(buf, "{expr_behind_ref}.{method}()"); } else if let ast::Expr::RangeExpr(..) = iterable { // range expressions need to be parenthesized for the syntax to be correct - format_to!(buf, "({})", iterable); + format_to!(buf, "({iterable})"); } else if impls_core_iter(&ctx.sema, &iterable) { - format_to!(buf, "{}", iterable); + format_to!(buf, "{iterable}"); } else if let ast::Expr::RefExpr(_) = iterable { - format_to!(buf, "({}).into_iter()", iterable); + format_to!(buf, "({iterable}).into_iter()"); } else { - format_to!(buf, "{}.into_iter()", iterable); + format_to!(buf, "{iterable}.into_iter()"); } - format_to!(buf, ".for_each(|{}| {});", pat, body); + format_to!(buf, ".for_each(|{pat}| {body});"); builder.replace(for_loop.syntax().text_range(), buf) }, diff --git a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs index 00095de257d5..c82a3b530325 100644 --- a/crates/ide-assists/src/handlers/convert_let_else_to_match.rs +++ b/crates/ide-assists/src/handlers/convert_let_else_to_match.rs @@ -80,7 +80,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String { .map( |(ident, ismut)| { if *ismut && addmut { - format!("mut {}", ident) + format!("mut {ident}") } else { ident.to_string() } @@ -93,7 +93,7 @@ fn binders_to_str(binders: &[(Name, bool)], addmut: bool) -> String { } else if binders.len() == 1 { vars } else { - format!("({})", vars) + format!("({vars})") } } @@ -153,7 +153,7 @@ pub(crate) fn convert_let_else_to_match(acc: &mut Assists, ctx: &AssistContext<' let only_expr = let_else_block.statements().next().is_none(); let branch2 = match &let_else_block.tail_expr() { - Some(tail) if only_expr => format!("{},", tail.syntax().text()), + Some(tail) if only_expr => format!("{tail},"), _ => let_else_block.syntax().text().to_string(), }; let replace = if binders.is_empty() { diff --git a/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs new file mode 100644 index 000000000000..5bf04a3ad371 --- /dev/null +++ b/crates/ide-assists/src/handlers/convert_match_to_let_else.rs @@ -0,0 +1,413 @@ +use ide_db::defs::{Definition, NameRefClass}; +use syntax::{ + ast::{self, HasName}, + ted, AstNode, SyntaxNode, +}; + +use crate::{ + assist_context::{AssistContext, Assists}, + AssistId, AssistKind, +}; + +// Assist: convert_match_to_let_else +// +// Converts let statement with match initializer to let-else statement. +// +// ``` +// # //- minicore: option +// fn foo(opt: Option<()>) { +// let val = $0match opt { +// Some(it) => it, +// None => return, +// }; +// } +// ``` +// -> +// ``` +// fn foo(opt: Option<()>) { +// let Some(val) = opt else { return }; +// } +// ``` +pub(crate) fn convert_match_to_let_else(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + let let_stmt: ast::LetStmt = ctx.find_node_at_offset()?; + let binding = find_binding(let_stmt.pat()?)?; + + let initializer = match let_stmt.initializer() { + Some(ast::Expr::MatchExpr(it)) => it, + _ => return None, + }; + let initializer_expr = initializer.expr()?; + + let (extracting_arm, diverging_arm) = match find_arms(ctx, &initializer) { + Some(it) => it, + None => return None, + }; + if extracting_arm.guard().is_some() { + cov_mark::hit!(extracting_arm_has_guard); + return None; + } + + let diverging_arm_expr = diverging_arm.expr()?; + let extracting_arm_pat = extracting_arm.pat()?; + let extracted_variable = find_extracted_variable(ctx, &extracting_arm)?; + + acc.add( + AssistId("convert_match_to_let_else", AssistKind::RefactorRewrite), + "Convert match to let-else", + let_stmt.syntax().text_range(), + |builder| { + let extracting_arm_pat = rename_variable(&extracting_arm_pat, extracted_variable, binding); + builder.replace( + let_stmt.syntax().text_range(), + format!("let {extracting_arm_pat} = {initializer_expr} else {{ {diverging_arm_expr} }};") + ) + }, + ) +} + +// Given a pattern, find the name introduced to the surrounding scope. +fn find_binding(pat: ast::Pat) -> Option { + if let ast::Pat::IdentPat(ident) = pat { + Some(ident) + } else { + None + } +} + +// Given a match expression, find extracting and diverging arms. +fn find_arms( + ctx: &AssistContext<'_>, + match_expr: &ast::MatchExpr, +) -> Option<(ast::MatchArm, ast::MatchArm)> { + let arms = match_expr.match_arm_list()?.arms().collect::>(); + if arms.len() != 2 { + return None; + } + + let mut extracting = None; + let mut diverging = None; + for arm in arms { + if ctx.sema.type_of_expr(&arm.expr().unwrap()).unwrap().original().is_never() { + diverging = Some(arm); + } else { + extracting = Some(arm); + } + } + + match (extracting, diverging) { + (Some(extracting), Some(diverging)) => Some((extracting, diverging)), + _ => { + cov_mark::hit!(non_diverging_match); + None + } + } +} + +// Given an extracting arm, find the extracted variable. +fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Option { + match arm.expr()? { + ast::Expr::PathExpr(path) => { + let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?; + match NameRefClass::classify(&ctx.sema, &name_ref)? { + NameRefClass::Definition(Definition::Local(local)) => { + let source = local.source(ctx.db()).value.left()?; + Some(source.name()?) + } + _ => None, + } + } + _ => { + cov_mark::hit!(extracting_arm_is_not_an_identity_expr); + return None; + } + } +} + +// Rename `extracted` with `binding` in `pat`. +fn rename_variable(pat: &ast::Pat, extracted: ast::Name, binding: ast::IdentPat) -> SyntaxNode { + let syntax = pat.syntax().clone_for_update(); + let extracted_syntax = syntax.covering_element(extracted.syntax().text_range()); + + // If `extracted` variable is a record field, we should rename it to `binding`, + // otherwise we just need to replace `extracted` with `binding`. + + if let Some(record_pat_field) = extracted_syntax.ancestors().find_map(ast::RecordPatField::cast) + { + if let Some(name_ref) = record_pat_field.field_name() { + ted::replace( + record_pat_field.syntax(), + ast::make::record_pat_field(ast::make::name_ref(&name_ref.text()), binding.into()) + .syntax() + .clone_for_update(), + ); + } + } else { + ted::replace(extracted_syntax, binding.syntax().clone_for_update()); + } + + syntax +} + +#[cfg(test)] +mod tests { + use crate::tests::{check_assist, check_assist_not_applicable}; + + use super::*; + + #[test] + fn should_not_be_applicable_for_non_diverging_match() { + cov_mark::check!(non_diverging_match); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => (), + }; +} +"#, + ); + } + + #[test] + fn should_not_be_applicable_if_extracting_arm_is_not_an_identity_expr() { + cov_mark::check_count!(extracting_arm_is_not_an_identity_expr, 2); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option) { + let val = $0match opt { + Some(it) => it + 1, + None => return, + }; +} +"#, + ); + + check_assist_not_applicable( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => { + let _ = 1 + 1; + it + }, + None => return, + }; +} +"#, + ); + } + + #[test] + fn should_not_be_applicable_if_extracting_arm_has_guard() { + cov_mark::check!(extracting_arm_has_guard); + check_assist_not_applicable( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) if 2 > 1 => it, + None => return, + }; +} +"#, + ); + } + + #[test] + fn basic_pattern() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => return, + }; +} + "#, + r#" +fn foo(opt: Option<()>) { + let Some(val) = opt else { return }; +} + "#, + ); + } + + #[test] + fn keeps_modifiers() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + let ref mut val = $0match opt { + Some(it) => it, + None => return, + }; +} + "#, + r#" +fn foo(opt: Option<()>) { + let Some(ref mut val) = opt else { return }; +} + "#, + ); + } + + #[test] + fn nested_pattern() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option, result +fn foo(opt: Option>) { + let val = $0match opt { + Some(Ok(it)) => it, + _ => return, + }; +} + "#, + r#" +fn foo(opt: Option>) { + let Some(Ok(val)) = opt else { return }; +} + "#, + ); + } + + #[test] + fn works_with_any_diverging_block() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => break, + }; + } +} + "#, + r#" +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { break }; + } +} + "#, + ); + + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => continue, + }; + } +} + "#, + r#" +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { continue }; + } +} + "#, + ); + + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn panic() -> ! {} + +fn foo(opt: Option<()>) { + loop { + let val = $0match opt { + Some(it) => it, + None => panic(), + }; + } +} + "#, + r#" +fn panic() -> ! {} + +fn foo(opt: Option<()>) { + loop { + let Some(val) = opt else { panic() }; + } +} + "#, + ); + } + + #[test] + fn struct_pattern() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +struct Point { + x: i32, + y: i32, +} + +fn foo(opt: Option) { + let val = $0match opt { + Some(Point { x: 0, y }) => y, + _ => return, + }; +} + "#, + r#" +struct Point { + x: i32, + y: i32, +} + +fn foo(opt: Option) { + let Some(Point { x: 0, y: val }) = opt else { return }; +} + "#, + ); + } + + #[test] + fn renames_whole_binding() { + check_assist( + convert_match_to_let_else, + r#" +//- minicore: option +fn foo(opt: Option) -> Option { + let val = $0match opt { + it @ Some(42) => it, + _ => return None, + }; + val +} + "#, + r#" +fn foo(opt: Option) -> Option { + let val @ Some(42) = opt else { return None }; + val +} + "#, + ); + } +} diff --git a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index cb75619ced9c..b97be34c5f7e 100644 --- a/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -129,32 +129,15 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext<' } Some((path, bound_ident)) => { // If-let. - let match_expr = { - let happy_arm = { - let pat = make::tuple_struct_pat( - path, - once(make::ext::simple_ident_pat(make::name("it")).into()), - ); - let expr = { - let path = make::ext::ident_path("it"); - make::expr_path(path) - }; - make::match_arm(once(pat.into()), None, expr) - }; - - let sad_arm = make::match_arm( - // FIXME: would be cool to use `None` or `Err(_)` if appropriate - once(make::wildcard_pat().into()), - None, - early_expression, - ); - - make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm])) - }; - - let let_stmt = make::let_stmt(bound_ident, None, Some(match_expr)); - let let_stmt = let_stmt.indent(if_indent_level); - let_stmt.syntax().clone_for_update() + let pat = make::tuple_struct_pat(path, once(bound_ident)); + let let_else_stmt = make::let_else_stmt( + pat.into(), + None, + cond_expr, + ast::make::tail_only_block_expr(early_expression), + ); + let let_else_stmt = let_else_stmt.indent(if_indent_level); + let_else_stmt.syntax().clone_for_update() } }; @@ -238,10 +221,7 @@ fn main(n: Option) { r#" fn main(n: Option) { bar(); - let n = match n { - Some(it) => it, - _ => return, - }; + let Some(n) = n else { return }; foo(n); // comment @@ -264,10 +244,7 @@ fn main() { "#, r#" fn main() { - let x = match Err(92) { - Ok(it) => it, - _ => return, - }; + let Ok(x) = Err(92) else { return }; foo(x); } "#, @@ -292,10 +269,7 @@ fn main(n: Option) { r#" fn main(n: Option) { bar(); - let n = match n { - Some(it) => it, - _ => return, - }; + let Some(n) = n else { return }; foo(n); // comment @@ -323,10 +297,7 @@ fn main(n: Option) { r#" fn main(n: Option) { bar(); - let mut n = match n { - Some(it) => it, - _ => return, - }; + let Some(mut n) = n else { return }; foo(n); // comment @@ -354,10 +325,7 @@ fn main(n: Option<&str>) { r#" fn main(n: Option<&str>) { bar(); - let ref n = match n { - Some(it) => it, - _ => return, - }; + let Some(ref n) = n else { return }; foo(n); // comment @@ -412,10 +380,7 @@ fn main() { r#" fn main() { while true { - let n = match n { - Some(it) => it, - _ => continue, - }; + let Some(n) = n else { continue }; foo(n); bar(); } @@ -469,10 +434,7 @@ fn main() { r#" fn main() { loop { - let n = match n { - Some(it) => it, - _ => continue, - }; + let Some(n) = n else { continue }; foo(n); bar(); } diff --git a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs index d8f522708460..92e091fca126 100644 --- a/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs +++ b/crates/ide-assists/src/handlers/convert_tuple_struct_to_named_struct.rs @@ -226,7 +226,13 @@ fn edit_field_references( } fn generate_names(fields: impl Iterator) -> Vec { - fields.enumerate().map(|(i, _)| ast::make::name(&format!("field{}", i + 1))).collect() + fields + .enumerate() + .map(|(i, _)| { + let idx = i + 1; + ast::make::name(&format!("field{idx}")) + }) + .collect() } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs index 54a7f480a4e4..b1b0f587cd33 100644 --- a/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs +++ b/crates/ide-assists/src/handlers/convert_two_arm_bool_match_to_matches_macro.rs @@ -58,16 +58,16 @@ pub(crate) fn convert_two_arm_bool_match_to_matches_macro( target_range, |builder| { let mut arm_str = String::new(); - if let Some(ref pat) = first_arm.pat() { + if let Some(pat) = &first_arm.pat() { arm_str += &pat.to_string(); } - if let Some(ref guard) = first_arm.guard() { - arm_str += &format!(" {}", &guard.to_string()); + if let Some(guard) = &first_arm.guard() { + arm_str += &format!(" {guard}"); } if invert_matches { - builder.replace(target_range, format!("!matches!({}, {})", expr, arm_str)); + builder.replace(target_range, format!("!matches!({expr}, {arm_str})")); } else { - builder.replace(target_range, format!("matches!({}, {})", expr, arm_str)); + builder.replace(target_range, format!("matches!({expr}, {arm_str})")); } }, ) diff --git a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs index dc581ff3bd2c..31c2ce7c1b54 100644 --- a/crates/ide-assists/src/handlers/destructure_tuple_binding.rs +++ b/crates/ide-assists/src/handlers/destructure_tuple_binding.rs @@ -133,7 +133,7 @@ fn generate_name( _usages: &Option, ) -> String { // FIXME: detect if name already used - format!("_{}", index) + format!("_{index}") } enum RefType { @@ -168,12 +168,12 @@ fn edit_tuple_assignment( let add_cursor = |text: &str| { // place cursor on first tuple item let first_tuple = &data.field_names[0]; - text.replacen(first_tuple, &format!("$0{}", first_tuple), 1) + text.replacen(first_tuple, &format!("$0{first_tuple}"), 1) }; // with sub_pattern: keep original tuple and add subpattern: `tup @ (_0, _1)` if in_sub_pattern { - let text = format!(" @ {}", tuple_pat); + let text = format!(" @ {tuple_pat}"); match ctx.config.snippet_cap { Some(cap) => { let snip = add_cursor(&text); @@ -314,9 +314,9 @@ struct RefData { impl RefData { fn format(&self, field_name: &str) -> String { match (self.needs_deref, self.needs_parentheses) { - (true, true) => format!("(*{})", field_name), - (true, false) => format!("*{}", field_name), - (false, true) => format!("({})", field_name), + (true, true) => format!("(*{field_name})"), + (true, false) => format!("*{field_name}"), + (false, true) => format!("({field_name})"), (false, false) => field_name.to_string(), } } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index d6c8ea785f84..060588358492 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -181,7 +181,7 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef let mut counter = 0; while names_in_scope.contains(&name) { counter += 1; - name = format!("{}{}", &default_name, counter) + name = format!("{default_name}{counter}") } make::name_ref(&name) } @@ -1291,19 +1291,23 @@ fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> St match fun.outliving_locals.as_slice() { [] => {} [var] => { - format_to!(buf, "let {}{} = ", mut_modifier(var), var.local.name(ctx.db())) + let modifier = mut_modifier(var); + let name = var.local.name(ctx.db()); + format_to!(buf, "let {modifier}{name} = ") } vars => { buf.push_str("let ("); let bindings = vars.iter().format_with(", ", |local, f| { - f(&format_args!("{}{}", mut_modifier(local), local.local.name(ctx.db()))) + let modifier = mut_modifier(local); + let name = local.local.name(ctx.db()); + f(&format_args!("{modifier}{name}")) }); - format_to!(buf, "{}", bindings); + format_to!(buf, "{bindings}"); buf.push_str(") = "); } } - format_to!(buf, "{}", expr); + format_to!(buf, "{expr}"); let insert_comma = fun .body .parent() @@ -1447,6 +1451,8 @@ fn format_function( new_indent: IndentLevel, ) -> String { let mut fn_def = String::new(); + + let fun_name = &fun.name; let params = fun.make_param_list(ctx, module); let ret_ty = fun.make_ret_ty(ctx, module); let body = make_body(ctx, old_indent, new_indent, fun); @@ -1454,42 +1460,28 @@ fn format_function( let async_kw = if fun.control_flow.is_async { "async " } else { "" }; let unsafe_kw = if fun.control_flow.is_unsafe { "unsafe " } else { "" }; let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); + + format_to!(fn_def, "\n\n{new_indent}{const_kw}{async_kw}{unsafe_kw}"); match ctx.config.snippet_cap { - Some(_) => format_to!( - fn_def, - "\n\n{}{}{}{}fn $0{}", - new_indent, - const_kw, - async_kw, - unsafe_kw, - fun.name, - ), - None => format_to!( - fn_def, - "\n\n{}{}{}{}fn {}", - new_indent, - const_kw, - async_kw, - unsafe_kw, - fun.name, - ), + Some(_) => format_to!(fn_def, "fn $0{fun_name}"), + None => format_to!(fn_def, "fn {fun_name}"), } if let Some(generic_params) = generic_params { - format_to!(fn_def, "{}", generic_params); + format_to!(fn_def, "{generic_params}"); } - format_to!(fn_def, "{}", params); + format_to!(fn_def, "{params}"); if let Some(ret_ty) = ret_ty { - format_to!(fn_def, " {}", ret_ty); + format_to!(fn_def, " {ret_ty}"); } if let Some(where_clause) = where_clause { - format_to!(fn_def, " {}", where_clause); + format_to!(fn_def, " {where_clause}"); } - format_to!(fn_def, " {}", body); + format_to!(fn_def, " {body}"); fn_def } diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index 897980c66504..56834394aeba 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -127,7 +127,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti for item in items_to_be_processed { let item = item.indent(IndentLevel(1)); let mut indented_item = String::new(); - format_to!(indented_item, "{}{}", new_item_indent, item.to_string()); + format_to!(indented_item, "{new_item_indent}{item}"); body_items.push(indented_item); } @@ -137,30 +137,28 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let mut impl_body_def = String::new(); if let Some(self_ty) = impl_.self_ty() { - format_to!( - impl_body_def, - "{}impl {} {{\n{}\n{}}}", - old_item_indent + 1, - self_ty.to_string(), - body, - old_item_indent + 1 - ); - + { + let impl_indent = old_item_indent + 1; + format_to!( + impl_body_def, + "{impl_indent}impl {self_ty} {{\n{body}\n{impl_indent}}}", + ); + } body = impl_body_def; // Add the import for enum/struct corresponding to given impl block module.make_use_stmt_of_node_with_super(self_ty.syntax()); for item in module.use_items { - let mut indented_item = String::new(); - format_to!(indented_item, "{}{}", old_item_indent + 1, item.to_string()); - body = format!("{}\n\n{}", indented_item, body); + let item_indent = old_item_indent + 1; + body = format!("{item_indent}{item}\n\n{body}"); } } } let mut module_def = String::new(); - format_to!(module_def, "mod {} {{\n{}\n{}}}", module.name, body, old_item_indent); + let module_name = module.name; + format_to!(module_def, "mod {module_name} {{\n{body}\n{old_item_indent}}}"); let mut usages_to_be_updated_for_curr_file = vec![]; for usages_to_be_updated_for_file in usages_to_be_processed { @@ -199,7 +197,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti builder.delete(range); } - builder.insert(impl_.syntax().text_range().end(), format!("\n\n{}", module_def)); + builder.insert(impl_.syntax().text_range().end(), format!("\n\n{module_def}")); } else { builder.replace(module.text_range, module_def) } @@ -343,9 +341,10 @@ impl Module { && !self.text_range.contains_range(desc.text_range()) { if let Some(name_ref) = ast::NameRef::cast(desc) { + let mod_name = self.name; return Some(( name_ref.syntax().text_range(), - format!("{}::{}", self.name, name_ref), + format!("{mod_name}::{name_ref}"), )); } } diff --git a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs index 970e948dfd93..b4e10667b07a 100644 --- a/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs +++ b/crates/ide-assists/src/handlers/extract_struct_from_enum_variant.rs @@ -296,10 +296,14 @@ fn create_struct_def( fn update_variant(variant: &ast::Variant, generics: Option) -> Option<()> { let name = variant.name()?; - let ty = generics + let generic_args = generics .filter(|generics| generics.generic_params().count() > 0) - .map(|generics| make::ty(&format!("{}{}", &name.text(), generics.to_generic_args()))) - .unwrap_or_else(|| make::ty(&name.text())); + .map(|generics| generics.to_generic_args()); + // FIXME: replace with a `ast::make` constructor + let ty = match generic_args { + Some(generic_args) => make::ty(&format!("{name}{generic_args}")), + None => make::ty(&name.text()), + }; // change from a record to a tuple field list let tuple_field = make::tuple_field(None, ty); diff --git a/crates/ide-assists/src/handlers/extract_type_alias.rs b/crates/ide-assists/src/handlers/extract_type_alias.rs index 03aa8601d14e..3116935fc5e7 100644 --- a/crates/ide-assists/src/handlers/extract_type_alias.rs +++ b/crates/ide-assists/src/handlers/extract_type_alias.rs @@ -1,8 +1,7 @@ use either::Either; use ide_db::syntax_helpers::node_ext::walk_ty; -use itertools::Itertools; use syntax::{ - ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName}, + ast::{self, edit::IndentLevel, make, AstNode, HasGenericParams, HasName}, match_ast, }; @@ -64,41 +63,29 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> known_generics.extend(it.generic_params()); } let generics = collect_used_generics(&ty, &known_generics); + let generic_params = + generics.map(|it| make::generic_param_list(it.into_iter().cloned())); - let replacement = if !generics.is_empty() { - format!( - "Type<{}>", - generics.iter().format_with(", ", |generic, f| { - match generic { - ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()), - ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()), - ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()), - } - }) - ) - } else { - String::from("Type") - }; + let ty_args = generic_params + .as_ref() + .map_or(String::new(), |it| it.to_generic_args().to_string()); + let replacement = format!("Type{ty_args}"); builder.replace(target, replacement); let indent = IndentLevel::from_node(node); - let generics = if !generics.is_empty() { - format!("<{}>", generics.iter().format(", ")) - } else { - String::new() - }; + let generic_params = generic_params.map_or(String::new(), |it| it.to_string()); match ctx.config.snippet_cap { Some(cap) => { builder.insert_snippet( cap, insert_pos, - format!("type $0Type{} = {};\n\n{}", generics, ty, indent), + format!("type $0Type{generic_params} = {ty};\n\n{indent}"), ); } None => { builder.insert( insert_pos, - format!("type Type{} = {};\n\n{}", generics, ty, indent), + format!("type Type{generic_params} = {ty};\n\n{indent}"), ); } } @@ -109,7 +96,7 @@ pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext<'_>) -> fn collect_used_generics<'gp>( ty: &ast::Type, known_generics: &'gp [ast::GenericParam], -) -> Vec<&'gp ast::GenericParam> { +) -> Option> { // can't use a closure -> closure here cause lifetime inference fails for that fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ { move |gp: &&ast::GenericParam| match gp { @@ -198,7 +185,8 @@ fn collect_used_generics<'gp>( ast::GenericParam::LifetimeParam(_) => 0, ast::GenericParam::TypeParam(_) => 1, }); - generics + + Some(generics).filter(|it| it.len() > 0) } #[cfg(test)] diff --git a/crates/ide-assists/src/handlers/extract_variable.rs b/crates/ide-assists/src/handlers/extract_variable.rs index 3596b6f82381..a738deffb95b 100644 --- a/crates/ide-assists/src/handlers/extract_variable.rs +++ b/crates/ide-assists/src/handlers/extract_variable.rs @@ -91,13 +91,13 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match anchor { Anchor::Before(_) | Anchor::Replace(_) => { - format_to!(buf, "let {}{} = {}", var_modifier, var_name, reference_modifier) + format_to!(buf, "let {var_modifier}{var_name} = {reference_modifier}") } Anchor::WrapInBlock(_) => { - format_to!(buf, "{{ let {} = {}", var_name, reference_modifier) + format_to!(buf, "{{ let {var_name} = {reference_modifier}") } }; - format_to!(buf, "{}", to_extract.syntax()); + format_to!(buf, "{to_extract}"); if let Anchor::Replace(stmt) = anchor { cov_mark::hit!(test_extract_var_expr_stmt); @@ -107,8 +107,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match ctx.config.snippet_cap { Some(cap) => { let snip = buf.replace( - &format!("let {}{}", var_modifier, var_name), - &format!("let {}$0{}", var_modifier, var_name), + &format!("let {var_modifier}{var_name}"), + &format!("let {var_modifier}$0{var_name}"), ); edit.replace_snippet(cap, expr_range, snip) } @@ -135,8 +135,8 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op match ctx.config.snippet_cap { Some(cap) => { let snip = buf.replace( - &format!("let {}{}", var_modifier, var_name), - &format!("let {}$0{}", var_modifier, var_name), + &format!("let {var_modifier}{var_name}"), + &format!("let {var_modifier}$0{var_name}"), ); edit.insert_snippet(cap, offset, snip) } diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index b33846f54665..876454302870 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -57,8 +57,8 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) if current_module.krate() == target_module.krate() { "pub(crate)" } else { "pub" }; let assist_label = match target_name { - None => format!("Change visibility to {}", missing_visibility), - Some(name) => format!("Change visibility of {} to {}", name, missing_visibility), + None => format!("Change visibility to {missing_visibility}"), + Some(name) => format!("Change visibility of {name} to {missing_visibility}"), }; acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { @@ -68,15 +68,15 @@ fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) Some(current_visibility) => builder.replace_snippet( cap, current_visibility.syntax().text_range(), - format!("$0{}", missing_visibility), + format!("$0{missing_visibility}"), ), - None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), }, None => match current_visibility { Some(current_visibility) => { builder.replace(current_visibility.syntax().text_range(), missing_visibility) } - None => builder.insert(offset, format!("{} ", missing_visibility)), + None => builder.insert(offset, format!("{missing_visibility} ")), }, } }) @@ -114,7 +114,7 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> let target_name = record_field_def.name(ctx.db()); let assist_label = - format!("Change visibility of {}.{} to {}", parent_name, target_name, missing_visibility); + format!("Change visibility of {parent_name}.{target_name} to {missing_visibility}"); acc.add(AssistId("fix_visibility", AssistKind::QuickFix), assist_label, target, |builder| { builder.edit_file(target_file); @@ -123,15 +123,15 @@ fn add_vis_to_referenced_record_field(acc: &mut Assists, ctx: &AssistContext<'_> Some(current_visibility) => builder.replace_snippet( cap, current_visibility.syntax().text_range(), - format!("$0{}", missing_visibility), + format!("$0{missing_visibility}"), ), - None => builder.insert_snippet(cap, offset, format!("$0{} ", missing_visibility)), + None => builder.insert_snippet(cap, offset, format!("$0{missing_visibility} ")), }, None => match current_visibility { Some(current_visibility) => { builder.replace(current_visibility.syntax().text_range(), missing_visibility) } - None => builder.insert(offset, format!("{} ", missing_visibility)), + None => builder.insert(offset, format!("{missing_visibility} ")), }, } }) diff --git a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs index bdd3cf4f06c2..c9aa41c845ad 100644 --- a/crates/ide-assists/src/handlers/generate_enum_projection_method.rs +++ b/crates/ide-assists/src/handlers/generate_enum_projection_method.rs @@ -124,6 +124,7 @@ fn generate_enum_projection_method( happy_case, sad_case, } = props; + let variant = ctx.find_node_at_offset::()?; let variant_name = variant.name()?; let parent_enum = ast::Adt::Enum(variant.parent_enum()); @@ -144,7 +145,7 @@ fn generate_enum_projection_method( ast::StructKind::Unit => return None, }; - let fn_name = format!("{}_{}", fn_name_prefix, &to_lower_snake_case(&variant_name.text())); + let fn_name = format!("{fn_name_prefix}_{}", &to_lower_snake_case(&variant_name.text())); // Return early if we've found an existing new fn let impl_def = find_struct_impl(ctx, &parent_enum, &[fn_name.clone()])?; @@ -156,15 +157,25 @@ fn generate_enum_projection_method( assist_description, target, |builder| { - let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{v} ")); + let vis = parent_enum.visibility().map_or(String::new(), |v| format!("{} ", v)); + + let field_type_syntax = field_type.syntax(); + + let must_use = if ctx.config.assist_emit_must_use { + "#[must_use]\n " + } else { + "" + }; + let method = format!( - " {vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type}{return_suffix} {{ + " {must_use}{vis}fn {fn_name}({self_param}) -> {return_prefix}{field_type_syntax}{return_suffix} {{ if let Self::{variant_name}{pattern_suffix} = self {{ {happy_case}({bound_name}) }} else {{ {sad_case} }} - }}"); + }}" + ); add_method_to_adt(builder, &parent_enum, impl_def, &method); }, diff --git a/crates/ide-assists/src/handlers/inline_call.rs b/crates/ide-assists/src/handlers/inline_call.rs index 9f51cdaf8b1e..0c546ce5d41c 100644 --- a/crates/ide-assists/src/handlers/inline_call.rs +++ b/crates/ide-assists/src/handlers/inline_call.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeSet; + use ast::make; use either::Either; use hir::{db::HirDatabase, PathResolution, Semantics, TypeInfo}; @@ -190,10 +192,10 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option< PathResolution::Def(hir::ModuleDef::Function(f)) => f, _ => return None, }; - (function, format!("Inline `{}`", path)) + (function, format!("Inline `{path}`")) } ast::CallableExpr::MethodCall(call) => { - (ctx.sema.resolve_method_call(call)?, format!("Inline `{}`", name_ref)) + (ctx.sema.resolve_method_call(call)?, format!("Inline `{name_ref}`")) } }; @@ -373,8 +375,44 @@ fn inline( }) } } + + let mut func_let_vars: BTreeSet = BTreeSet::new(); + + // grab all of the local variable declarations in the function + for stmt in fn_body.statements() { + if let Some(let_stmt) = ast::LetStmt::cast(stmt.syntax().to_owned()) { + for has_token in let_stmt.syntax().children_with_tokens() { + if let Some(node) = has_token.as_node() { + if let Some(ident_pat) = ast::IdentPat::cast(node.to_owned()) { + func_let_vars.insert(ident_pat.syntax().text().to_string()); + } + } + } + } + } + // Inline parameter expressions or generate `let` statements depending on whether inlining works or not. for ((pat, param_ty, _), usages, expr) in izip!(params, param_use_nodes, arguments).rev() { + // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors + let usages: &[ast::PathExpr] = &*usages; + let expr: &ast::Expr = expr; + + let insert_let_stmt = || { + let ty = sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); + if let Some(stmt_list) = body.stmt_list() { + stmt_list.push_front( + make::let_stmt(pat.clone(), ty, Some(expr.clone())).clone_for_update().into(), + ) + } + }; + + // check if there is a local var in the function that conflicts with parameter + // if it does then emit a let statement and continue + if func_let_vars.contains(&expr.syntax().text().to_string()) { + insert_let_stmt(); + continue; + } + let inline_direct = |usage, replacement: &ast::Expr| { if let Some(field) = path_expr_as_record_field(usage) { cov_mark::hit!(inline_call_inline_direct_field); @@ -383,9 +421,7 @@ fn inline( ted::replace(usage.syntax(), &replacement.syntax().clone_for_update()); } }; - // izip confuses RA due to our lack of hygiene info currently losing us type info causing incorrect errors - let usages: &[ast::PathExpr] = &*usages; - let expr: &ast::Expr = expr; + match usages { // inline single use closure arguments [usage] @@ -408,18 +444,11 @@ fn inline( } // can't inline, emit a let statement _ => { - let ty = - sema.type_of_expr(expr).filter(TypeInfo::has_adjustment).and(param_ty.clone()); - if let Some(stmt_list) = body.stmt_list() { - stmt_list.push_front( - make::let_stmt(pat.clone(), ty, Some(expr.clone())) - .clone_for_update() - .into(), - ) - } + insert_let_stmt(); } } } + if let Some(generic_arg_list) = generic_arg_list.clone() { if let Some((target, source)) = &sema.scope(node.syntax()).zip(sema.scope(fn_body.syntax())) { @@ -1256,4 +1285,37 @@ impl A { "#, ) } + + #[test] + fn local_variable_shadowing_callers_argument() { + check_assist( + inline_call, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = foo$0(a, b); +} +"#, + r#" +fn foo(bar: u32, baz: u32) -> u32 { + let a = 1; + bar * baz * a * 6 +} +fn main() { + let a = 7; + let b = 1; + let res = { + let bar = a; + let a = 1; + bar * b * a * 6 + }; +} +"#, + ); + } } diff --git a/crates/ide-assists/src/handlers/inline_local_variable.rs b/crates/ide-assists/src/handlers/inline_local_variable.rs index 7259d6781941..ce44100e34be 100644 --- a/crates/ide-assists/src/handlers/inline_local_variable.rs +++ b/crates/ide-assists/src/handlers/inline_local_variable.rs @@ -113,7 +113,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) .collect::>>()?; let init_str = initializer_expr.syntax().text().to_string(); - let init_in_paren = format!("({})", &init_str); + let init_in_paren = format!("({init_str})"); let target = match target { ast::NameOrNameRef::Name(it) => it.syntax().text_range(), @@ -132,7 +132,7 @@ pub(crate) fn inline_local_variable(acc: &mut Assists, ctx: &AssistContext<'_>) let replacement = if should_wrap { &init_in_paren } else { &init_str }; if ast::RecordExprField::for_field_name(&name).is_some() { cov_mark::hit!(inline_field_shorthand); - builder.insert(range.end(), format!(": {}", replacement)); + builder.insert(range.end(), format!(": {replacement}")); } else { builder.replace(range, replacement.clone()) } diff --git a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs index 2fc754e3e50d..a54dc4f96de0 100644 --- a/crates/ide-assists/src/handlers/introduce_named_lifetime.rs +++ b/crates/ide-assists/src/handlers/introduce_named_lifetime.rs @@ -127,7 +127,7 @@ fn generate_unique_lifetime_param_name( Some(type_params) => { let used_lifetime_params: FxHashSet<_> = type_params.lifetime_params().map(|p| p.syntax().text().to_string()).collect(); - ('a'..='z').map(|it| format!("'{}", it)).find(|it| !used_lifetime_params.contains(it)) + ('a'..='z').map(|it| format!("'{it}")).find(|it| !used_lifetime_params.contains(it)) } None => Some("'a".to_string()), } diff --git a/crates/ide-assists/src/handlers/merge_match_arms.rs b/crates/ide-assists/src/handlers/merge_match_arms.rs index c24015b1c517..641c90885bf5 100644 --- a/crates/ide-assists/src/handlers/merge_match_arms.rs +++ b/crates/ide-assists/src/handlers/merge_match_arms.rs @@ -78,7 +78,7 @@ pub(crate) fn merge_match_arms(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op .join(" | ") }; - let arm = format!("{} => {},", pats, current_expr.syntax().text()); + let arm = format!("{pats} => {current_expr},"); if let [first, .., last] = &*arms_to_merge { let start = first.syntax().text_range().start(); diff --git a/crates/ide-assists/src/handlers/move_from_mod_rs.rs b/crates/ide-assists/src/handlers/move_from_mod_rs.rs index a6c85a2b18b3..1728c03cd03e 100644 --- a/crates/ide-assists/src/handlers/move_from_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_from_mod_rs.rs @@ -40,11 +40,11 @@ pub(crate) fn move_from_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let target = source_file.syntax().text_range(); let module_name = module.name(ctx.db())?.to_string(); - let path = format!("../{}.rs", module_name); + let path = format!("../{module_name}.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( AssistId("move_from_mod_rs", AssistKind::Refactor), - format!("Convert {}/mod.rs to {}.rs", module_name, module_name), + format!("Convert {module_name}/mod.rs to {module_name}.rs"), target, |builder| { builder.move_file(ctx.file_id(), dst); diff --git a/crates/ide-assists/src/handlers/move_guard.rs b/crates/ide-assists/src/handlers/move_guard.rs index b8f1b36deb93..ec3281619cc3 100644 --- a/crates/ide-assists/src/handlers/move_guard.rs +++ b/crates/ide-assists/src/handlers/move_guard.rs @@ -133,16 +133,16 @@ pub(crate) fn move_arm_cond_to_match_guard( }; let then_arm_end = match_arm.syntax().text_range().end(); let indent_level = match_arm.indent_level(); - let spaces = " ".repeat(indent_level.0 as _); + let spaces = indent_level; let mut first = true; for (cond, block) in conds_blocks { if !first { - edit.insert(then_arm_end, format!("\n{}", spaces)); + edit.insert(then_arm_end, format!("\n{spaces}")); } else { first = false; } - let guard = format!("{} if {} => ", match_pat, cond.syntax().text()); + let guard = format!("{match_pat} if {cond} => "); edit.insert(then_arm_end, guard); let only_expr = block.statements().next().is_none(); match &block.tail_expr() { @@ -158,7 +158,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } if let Some(e) = tail { cov_mark::hit!(move_guard_ifelse_else_tail); - let guard = format!("\n{}{} => ", spaces, match_pat); + let guard = format!("\n{spaces}{match_pat} => "); edit.insert(then_arm_end, guard); let only_expr = e.statements().next().is_none(); match &e.tail_expr() { @@ -183,7 +183,7 @@ pub(crate) fn move_arm_cond_to_match_guard( { cov_mark::hit!(move_guard_ifelse_has_wildcard); } - _ => edit.insert(then_arm_end, format!("\n{}{} => {{}}", spaces, match_pat)), + _ => edit.insert(then_arm_end, format!("\n{spaces}{match_pat} => {{}}")), } } }, diff --git a/crates/ide-assists/src/handlers/move_module_to_file.rs b/crates/ide-assists/src/handlers/move_module_to_file.rs index 7468318a594a..a7c605325ea6 100644 --- a/crates/ide-assists/src/handlers/move_module_to_file.rs +++ b/crates/ide-assists/src/handlers/move_module_to_file.rs @@ -52,7 +52,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> let mut buf = String::from("./"); match parent_module.name(ctx.db()) { Some(name) if !parent_module.is_mod_rs(ctx.db()) => { - format_to!(buf, "{}/", name) + format_to!(buf, "{name}/") } _ => (), } @@ -82,7 +82,7 @@ pub(crate) fn move_module_to_file(acc: &mut Assists, ctx: &AssistContext<'_>) -> items }; - let buf = format!("mod {};", module_name); + let buf = format!("mod {module_name};"); let replacement_start = match module_ast.mod_token() { Some(mod_token) => mod_token.text_range(), diff --git a/crates/ide-assists/src/handlers/move_to_mod_rs.rs b/crates/ide-assists/src/handlers/move_to_mod_rs.rs index a909ce8b2679..076d25411a81 100644 --- a/crates/ide-assists/src/handlers/move_to_mod_rs.rs +++ b/crates/ide-assists/src/handlers/move_to_mod_rs.rs @@ -40,11 +40,11 @@ pub(crate) fn move_to_mod_rs(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti let target = source_file.syntax().text_range(); let module_name = module.name(ctx.db())?.to_string(); - let path = format!("./{}/mod.rs", module_name); + let path = format!("./{module_name}/mod.rs"); let dst = AnchoredPathBuf { anchor: ctx.file_id(), path }; acc.add( AssistId("move_to_mod_rs", AssistKind::Refactor), - format!("Convert {}.rs to {}/mod.rs", module_name, module_name), + format!("Convert {module_name}.rs to {module_name}/mod.rs"), target, |builder| { builder.move_file(ctx.file_id(), dst); diff --git a/crates/ide-assists/src/handlers/number_representation.rs b/crates/ide-assists/src/handlers/number_representation.rs index 424db7437a74..7e3fef516bfd 100644 --- a/crates/ide-assists/src/handlers/number_representation.rs +++ b/crates/ide-assists/src/handlers/number_representation.rs @@ -38,7 +38,7 @@ pub(crate) fn reformat_number_literal(acc: &mut Assists, ctx: &AssistContext<'_> converted.push_str(suffix); let group_id = GroupLabel("Reformat number literal".into()); - let label = format!("Convert {} to {}", literal, converted); + let label = format!("Convert {literal} to {converted}"); let range = literal.syntax().text_range(); acc.add_group( &group_id, diff --git a/crates/ide-assists/src/handlers/qualify_method_call.rs b/crates/ide-assists/src/handlers/qualify_method_call.rs index e57d1d065d62..1ea87429c509 100644 --- a/crates/ide-assists/src/handlers/qualify_method_call.rs +++ b/crates/ide-assists/src/handlers/qualify_method_call.rs @@ -54,7 +54,7 @@ pub(crate) fn qualify_method_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> acc.add( AssistId("qualify_method_call", AssistKind::RefactorInline), - format!("Qualify `{}` method call", ident.text()), + format!("Qualify `{ident}` method call"), range, |builder| { qualify_candidate.qualify( diff --git a/crates/ide-assists/src/handlers/qualify_path.rs b/crates/ide-assists/src/handlers/qualify_path.rs index 4b2af550bc5e..e759e1561cbd 100644 --- a/crates/ide-assists/src/handlers/qualify_path.rs +++ b/crates/ide-assists/src/handlers/qualify_path.rs @@ -118,14 +118,14 @@ impl QualifyCandidate<'_> { match self { QualifyCandidate::QualifierStart(segment, generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{}{}::{}", import, generics, segment)); + replacer(format!("{import}{generics}::{segment}")); } QualifyCandidate::UnqualifiedName(generics) => { let generics = generics.as_ref().map_or_else(String::new, ToString::to_string); - replacer(format!("{}{}", import, generics)); + replacer(format!("{import}{generics}")); } QualifyCandidate::TraitAssocItem(qualifier, segment) => { - replacer(format!("<{} as {}>::{}", qualifier, import, segment)); + replacer(format!("<{qualifier} as {import}>::{segment}")); } QualifyCandidate::TraitMethod(db, mcall_expr) => { Self::qualify_trait_method(db, mcall_expr, replacer, import, item); @@ -155,16 +155,11 @@ impl QualifyCandidate<'_> { hir::Access::Exclusive => make::expr_ref(receiver, true), hir::Access::Owned => receiver, }; - replacer(format!( - "{}::{}{}{}", - import, - method_name, - generics, - match arg_list { - Some(args) => make::arg_list(iter::once(receiver).chain(args)), - None => make::arg_list(iter::once(receiver)), - } - )); + let arg_list = match arg_list { + Some(args) => make::arg_list(iter::once(receiver).chain(args)), + None => make::arg_list(iter::once(receiver)), + }; + replacer(format!("{import}::{method_name}{generics}{arg_list}")); } Some(()) } @@ -218,15 +213,17 @@ fn group_label(candidate: &ImportCandidate) -> GroupLabel { } } .text(); - GroupLabel(format!("Qualify {}", name)) + GroupLabel(format!("Qualify {name}")) } fn label(candidate: &ImportCandidate, import: &LocatedImport) -> String { + let import_path = &import.import_path; + match candidate { ImportCandidate::Path(candidate) if candidate.qualifier.is_none() => { - format!("Qualify as `{}`", import.import_path) + format!("Qualify as `{import_path}`") } - _ => format!("Qualify with `{}`", import.import_path), + _ => format!("Qualify with `{import_path}`"), } } diff --git a/crates/ide-assists/src/handlers/raw_string.rs b/crates/ide-assists/src/handlers/raw_string.rs index dbe8cb7bf031..c9bc25b27a5e 100644 --- a/crates/ide-assists/src/handlers/raw_string.rs +++ b/crates/ide-assists/src/handlers/raw_string.rs @@ -34,13 +34,10 @@ pub(crate) fn make_raw_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opt let hashes = "#".repeat(required_hashes(&value).max(1)); if matches!(value, Cow::Borrowed(_)) { // Avoid replacing the whole string to better position the cursor. - edit.insert(token.syntax().text_range().start(), format!("r{}", hashes)); + edit.insert(token.syntax().text_range().start(), format!("r{hashes}")); edit.insert(token.syntax().text_range().end(), hashes); } else { - edit.replace( - token.syntax().text_range(), - format!("r{}\"{}\"{}", hashes, value, hashes), - ); + edit.replace(token.syntax().text_range(), format!("r{hashes}\"{value}\"{hashes}")); } }, ) @@ -83,7 +80,7 @@ pub(crate) fn make_usual_string(acc: &mut Assists, ctx: &AssistContext<'_>) -> O } } - edit.replace(token.syntax().text_range(), format!("\"{}\"", escaped)); + edit.replace(token.syntax().text_range(), format!("\"{escaped}\"")); }, ) } diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index afaa7c933c73..3d9cbff177ba 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -102,7 +102,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( }; ( macro_call.syntax().text_range(), - if wrap { format!("({})", expr) } else { expr.to_string() }, + if wrap { format!("({expr})") } else { expr.to_string() }, ) } // dbg!(expr0, expr1, ...) @@ -127,8 +127,8 @@ mod tests { fn check(ra_fixture_before: &str, ra_fixture_after: &str) { check_assist( remove_dbg, - &format!("fn main() {{\n{}\n}}", ra_fixture_before), - &format!("fn main() {{\n{}\n}}", ra_fixture_after), + &format!("fn main() {{\n{ra_fixture_before}\n}}"), + &format!("fn main() {{\n{ra_fixture_after}\n}}"), ); } diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index 9fd5e1886d20..f9ba289ee175 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -124,7 +124,7 @@ fn add_assist( ) -> Option<()> { let target = attr.syntax().text_range(); let annotated_name = adt.name()?; - let label = format!("Convert to manual `impl {} for {}`", replace_trait_path, annotated_name); + let label = format!("Convert to manual `impl {replace_trait_path} for {annotated_name}`"); acc.add( AssistId("replace_derive_with_manual_impl", AssistKind::Refactor), @@ -158,11 +158,8 @@ fn add_assist( } } - builder.insert_snippet( - cap, - insert_pos, - format!("\n\n{}", render_snippet(cap, impl_def.syntax(), cursor)), - ) + let rendered = render_snippet(cap, impl_def.syntax(), cursor); + builder.insert_snippet(cap, insert_pos, format!("\n\n{rendered}")) } }; }, diff --git a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs index 7d91be621013..77382056c183 100644 --- a/crates/ide-assists/src/handlers/replace_or_with_or_else.rs +++ b/crates/ide-assists/src/handlers/replace_or_with_or_else.rs @@ -62,7 +62,7 @@ pub(crate) fn replace_or_with_or_else(acc: &mut Assists, ctx: &AssistContext<'_> acc.add( AssistId("replace_or_with_or_else", AssistKind::RefactorRewrite), - format!("Replace {} with {}", name.text(), replace), + format!("Replace {name} with {replace}"), call.syntax().text_range(), |builder| { builder.replace(name.syntax().text_range(), replace); @@ -138,7 +138,7 @@ pub(crate) fn replace_or_else_with_or(acc: &mut Assists, ctx: &AssistContext<'_> acc.add( AssistId("replace_or_else_with_or", AssistKind::RefactorRewrite), - format!("Replace {} with {}", name.text(), replace), + format!("Replace {name} with {replace}"), call.syntax().text_range(), |builder| { builder.replace(name.syntax().text_range(), replace); diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 521447c26dfb..c177adc7a10d 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -79,7 +79,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( "Replace turbofish with explicit type", TextRange::new(initializer_start, turbofish_range.end()), |builder| { - builder.insert(ident_range.end(), format!(": {}", returned_type)); + builder.insert(ident_range.end(), format!(": {returned_type}")); builder.delete(turbofish_range); }, ); diff --git a/crates/ide-assists/src/handlers/unnecessary_async.rs b/crates/ide-assists/src/handlers/unnecessary_async.rs index d5cd2d551349..043988322533 100644 --- a/crates/ide-assists/src/handlers/unnecessary_async.rs +++ b/crates/ide-assists/src/handlers/unnecessary_async.rs @@ -44,6 +44,12 @@ pub(crate) fn unnecessary_async(acc: &mut Assists, ctx: &AssistContext<'_>) -> O if function.body()?.syntax().descendants().find_map(ast::AwaitExpr::cast).is_some() { return None; } + // Do nothing if the method is a member of trait. + if let Some(impl_) = function.syntax().ancestors().nth(2).and_then(ast::Impl::cast) { + if let Some(_) = impl_.trait_() { + return None; + } + } // Remove the `async` keyword plus whitespace after it, if any. let async_range = { @@ -254,4 +260,18 @@ pub async fn f(s: &S) { s.f2() }"#, fn does_not_apply_when_not_on_prototype() { check_assist_not_applicable(unnecessary_async, "pub async fn f() { $0f2() }") } + + #[test] + fn does_not_apply_on_async_trait_method() { + check_assist_not_applicable( + unnecessary_async, + r#" +trait Trait { + async fn foo(); +} +impl Trait for () { + $0async fn foo() {} +}"#, + ); + } } diff --git a/crates/ide-assists/src/handlers/unwrap_tuple.rs b/crates/ide-assists/src/handlers/unwrap_tuple.rs index 25c58d086e97..d09614c51127 100644 --- a/crates/ide-assists/src/handlers/unwrap_tuple.rs +++ b/crates/ide-assists/src/handlers/unwrap_tuple.rs @@ -69,13 +69,13 @@ pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option for (pat, ty, expr) in itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents)) + zipped_decls.push_str(&format!("{indents}let {pat}: {ty} = {expr};\n")) } edit.replace(parent.text_range(), zipped_decls.trim()); } else { let mut zipped_decls = String::new(); for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) { - zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents)); + zipped_decls.push_str(&format!("{indents}let {pat} = {expr};\n")); } edit.replace(parent.text_range(), zipped_decls.trim()); } 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 83446387db1c..b6c489eb62ee 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 @@ -76,11 +76,11 @@ pub(crate) fn wrap_return_type_in_result(acc: &mut Assists, ctx: &AssistContext< match ctx.config.snippet_cap { Some(cap) => { - let snippet = format!("Result<{}, ${{0:_}}>", type_ref); + let snippet = format!("Result<{type_ref}, ${{0:_}}>"); builder.replace_snippet(cap, type_ref.syntax().text_range(), snippet) } None => builder - .replace(type_ref.syntax().text_range(), format!("Result<{}, _>", type_ref)), + .replace(type_ref.syntax().text_range(), format!("Result<{type_ref}, _>")), } }, ) diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index a07318cefad2..387cc6314282 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -120,6 +120,7 @@ mod handlers { mod convert_into_to_from; mod convert_iter_for_each_to_for; mod convert_let_else_to_match; + mod convert_match_to_let_else; mod convert_tuple_struct_to_named_struct; mod convert_named_struct_to_tuple_struct; mod convert_to_guarded_return; @@ -220,6 +221,7 @@ mod handlers { convert_iter_for_each_to_for::convert_for_loop_with_for_each, convert_let_else_to_match::convert_let_else_to_match, convert_named_struct_to_tuple_struct::convert_named_struct_to_tuple_struct, + convert_match_to_let_else::convert_match_to_let_else, convert_to_guarded_return::convert_to_guarded_return, convert_tuple_struct_to_named_struct::convert_tuple_struct_to_named_struct, convert_two_arm_bool_match_to_matches_macro::convert_two_arm_bool_match_to_matches_macro, diff --git a/crates/ide-assists/src/tests.rs b/crates/ide-assists/src/tests.rs index f7f2417d0745..92ced27c78ae 100644 --- a/crates/ide-assists/src/tests.rs +++ b/crates/ide-assists/src/tests.rs @@ -30,6 +30,7 @@ pub(crate) const TEST_CONFIG: AssistConfig = AssistConfig { skip_glob_imports: true, }, prefer_no_std: false, + assist_emit_must_use: false, }; pub(crate) fn with_single_file(text: &str) -> (RootDatabase, FileId) { diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 2c4000efe0fa..029d169899bb 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -407,6 +407,27 @@ fn main() { ) } +#[test] +fn doctest_convert_match_to_let_else() { + check_doc_test( + "convert_match_to_let_else", + r#####" +//- minicore: option +fn foo(opt: Option<()>) { + let val = $0match opt { + Some(it) => it, + None => return, + }; +} +"#####, + r#####" +fn foo(opt: Option<()>) { + let Some(val) = opt else { return }; +} +"#####, + ) +} + #[test] fn doctest_convert_named_struct_to_tuple_struct() { check_doc_test( diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index db32e7182c44..307e67927056 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -189,8 +189,8 @@ pub(crate) fn render_snippet(_cap: SnippetCap, node: &SyntaxNode, cursor: Cursor let mut placeholder = cursor.node().to_string(); escape(&mut placeholder); let tab_stop = match cursor { - Cursor::Replace(placeholder) => format!("${{0:{}}}", placeholder), - Cursor::Before(placeholder) => format!("$0{}", placeholder), + Cursor::Replace(placeholder) => format!("${{0:{placeholder}}}"), + Cursor::Before(placeholder) => format!("$0{placeholder}"), }; let mut buf = node.to_string(); @@ -539,17 +539,17 @@ impl ReferenceConversion { ReferenceConversionType::AsRefSlice => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("&[{}]", type_argument_name) + format!("&[{type_argument_name}]") } ReferenceConversionType::Dereferenced => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("&{}", type_argument_name) + format!("&{type_argument_name}") } ReferenceConversionType::Option => { let type_argument_name = self.ty.type_arguments().next().unwrap().display(db).to_string(); - format!("Option<&{}>", type_argument_name) + format!("Option<&{type_argument_name}>") } ReferenceConversionType::Result => { let mut type_arguments = self.ty.type_arguments(); @@ -557,19 +557,19 @@ impl ReferenceConversion { type_arguments.next().unwrap().display(db).to_string(); let second_type_argument_name = type_arguments.next().unwrap().display(db).to_string(); - format!("Result<&{}, &{}>", first_type_argument_name, second_type_argument_name) + format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>") } } } pub(crate) fn getter(&self, field_name: String) -> String { match self.conversion { - ReferenceConversionType::Copy => format!("self.{}", field_name), + ReferenceConversionType::Copy => format!("self.{field_name}"), ReferenceConversionType::AsRefStr | ReferenceConversionType::AsRefSlice | ReferenceConversionType::Dereferenced | ReferenceConversionType::Option - | ReferenceConversionType::Result => format!("self.{}.as_ref()", field_name), + | ReferenceConversionType::Result => format!("self.{field_name}.as_ref()"), } } } diff --git a/crates/ide-assists/src/utils/gen_trait_fn_body.rs b/crates/ide-assists/src/utils/gen_trait_fn_body.rs index 7a0c912959a1..6c87e66c134d 100644 --- a/crates/ide-assists/src/utils/gen_trait_fn_body.rs +++ b/crates/ide-assists/src/utils/gen_trait_fn_body.rs @@ -41,7 +41,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?; + let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; match variant.field_list() { // => match self { Self::Name { x } => Self::Name { x: x.clone() } } @@ -70,7 +70,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut pats = vec![]; let mut fields = vec![]; for (i, _) in list.fields().enumerate() { - let field_name = format!("arg{}", i); + let field_name = format!("arg{i}"); let pat = make::ident_pat(false, false, make::name(&field_name)); pats.push(pat.into()); @@ -118,7 +118,7 @@ fn gen_clone_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut fields = vec![]; for (i, _) in field_list.fields().enumerate() { let f_path = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(f_path, &format!("{}", i)); + let target = make::expr_field(f_path, &format!("{i}")); fields.push(gen_clone_call(target)); } let struct_name = make::expr_path(make::ext::ident_path("Self")); @@ -151,7 +151,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut arms = vec![]; for variant in list.variants() { let name = variant.name()?; - let variant_name = make::ext::path_from_idents(["Self", &format!("{}", name)])?; + let variant_name = make::ext::path_from_idents(["Self", &format!("{name}")])?; let target = make::expr_path(make::ext::ident_path("f")); match variant.field_list() { @@ -159,7 +159,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => f.debug_struct(name) let target = make::expr_path(make::ext::ident_path("f")); let method = make::name_ref("debug_struct"); - let struct_name = format!("\"{}\"", name); + let struct_name = format!("\"{name}\""); let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); let mut expr = make::expr_method_call(target, method, args); @@ -173,8 +173,8 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => .field("field_name", field) let method_name = make::name_ref("field"); - let name = make::expr_literal(&(format!("\"{}\"", field_name))).into(); - let path = &format!("{}", field_name); + let name = make::expr_literal(&(format!("\"{field_name}\""))).into(); + let path = &format!("{field_name}"); let path = make::expr_path(make::ext::ident_path(path)); let args = make::arg_list(vec![name, path]); expr = make::expr_method_call(expr, method_name, args); @@ -192,13 +192,13 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { // => f.debug_tuple(name) let target = make::expr_path(make::ext::ident_path("f")); let method = make::name_ref("debug_tuple"); - let struct_name = format!("\"{}\"", name); + let struct_name = format!("\"{name}\""); let args = make::arg_list(Some(make::expr_literal(&struct_name).into())); let mut expr = make::expr_method_call(target, method, args); let mut pats = vec![]; for (i, _) in list.fields().enumerate() { - let name = format!("arg{}", i); + let name = format!("arg{i}"); // create a field pattern for use in `MyStruct(fields..)` let field_name = make::name(&name); @@ -222,7 +222,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { arms.push(make::match_arm(Some(pat.into()), None, expr)); } None => { - let fmt_string = make::expr_literal(&(format!("\"{}\"", name))).into(); + let fmt_string = make::expr_literal(&(format!("\"{name}\""))).into(); let args = make::arg_list([target, fmt_string]); let macro_name = make::expr_path(make::ext::ident_path("write")); let macro_call = make::expr_macro_call(macro_name, args); @@ -244,7 +244,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { } ast::Adt::Struct(strukt) => { - let name = format!("\"{}\"", annotated_name); + let name = format!("\"{annotated_name}\""); let args = make::arg_list(Some(make::expr_literal(&name).into())); let target = make::expr_path(make::ext::ident_path("f")); @@ -258,10 +258,10 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut expr = make::expr_method_call(target, method, args); for field in field_list.fields() { let name = field.name()?; - let f_name = make::expr_literal(&(format!("\"{}\"", name))).into(); + let f_name = make::expr_literal(&(format!("\"{name}\""))).into(); let f_path = make::expr_path(make::ext::ident_path("self")); let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{}", name)); + let f_path = make::expr_field(f_path, &format!("{name}")); let args = make::arg_list([f_name, f_path]); expr = make::expr_method_call(expr, make::name_ref("field"), args); } @@ -275,7 +275,7 @@ fn gen_debug_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { for (i, _) in field_list.fields().enumerate() { let f_path = make::expr_path(make::ext::ident_path("self")); let f_path = make::expr_ref(f_path, false); - let f_path = make::expr_field(f_path, &format!("{}", i)); + let f_path = make::expr_field(f_path, &format!("{i}")); let method = make::name_ref("field"); expr = make::expr_method_call(expr, method, make::arg_list(Some(f_path))); } @@ -379,7 +379,7 @@ fn gen_hash_impl(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut stmts = vec![]; for (i, _) in field_list.fields().enumerate() { let base = make::expr_path(make::ext::ident_path("self")); - let target = make::expr_field(base, &format!("{}", i)); + let target = make::expr_field(base, &format!("{i}")); stmts.push(gen_hash_call(target)); } make::block_expr(stmts, None).indent(ast::edit::IndentLevel(1)) @@ -453,10 +453,10 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { for field in list.fields() { let field_name = field.name()?.to_string(); - let l_name = &format!("l_{}", field_name); + let l_name = &format!("l_{field_name}"); l_fields.push(gen_record_pat_field(&field_name, l_name)); - let r_name = &format!("r_{}", field_name); + let r_name = &format!("r_{field_name}"); r_fields.push(gen_record_pat_field(&field_name, r_name)); let lhs = make::expr_path(make::ext::ident_path(l_name)); @@ -484,12 +484,12 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { let mut r_fields = vec![]; for (i, _) in list.fields().enumerate() { - let field_name = format!("{}", i); + let field_name = format!("{i}"); - let l_name = format!("l{}", field_name); + let l_name = format!("l{field_name}"); l_fields.push(gen_tuple_field(&l_name)); - let r_name = format!("r{}", field_name); + let r_name = format!("r{field_name}"); r_fields.push(gen_tuple_field(&r_name)); let lhs = make::expr_path(make::ext::ident_path(&l_name)); @@ -548,7 +548,7 @@ fn gen_partial_eq(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(ast::FieldList::TupleFieldList(field_list)) => { let mut expr = None; for (i, _) in field_list.fields().enumerate() { - let idx = format!("{}", i); + let idx = format!("{i}"); let lhs = make::expr_path(make::ext::ident_path("self")); let lhs = make::expr_field(lhs, &idx); let rhs = make::expr_path(make::ext::ident_path("other")); @@ -628,7 +628,7 @@ fn gen_partial_ord(adt: &ast::Adt, func: &ast::Fn) -> Option<()> { Some(ast::FieldList::TupleFieldList(field_list)) => { let mut exprs = vec![]; for (i, _) in field_list.fields().enumerate() { - let idx = format!("{}", i); + let idx = format!("{i}"); let lhs = make::expr_path(make::ext::ident_path("self")); let lhs = make::expr_field(lhs, &idx); let rhs = make::expr_path(make::ext::ident_path("other")); diff --git a/crates/ide-completion/src/completions/postfix.rs b/crates/ide-completion/src/completions/postfix.rs index 9a891cea2d45..b9bd47f7da50 100644 --- a/crates/ide-completion/src/completions/postfix.rs +++ b/crates/ide-completion/src/completions/postfix.rs @@ -69,10 +69,6 @@ pub(crate) fn complete_postfix( } } - if !ctx.config.snippets.is_empty() { - add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); - } - let try_enum = TryEnum::from_ty(&ctx.sema, &receiver_ty.strip_references()); if let Some(try_enum) = &try_enum { match try_enum { @@ -140,6 +136,10 @@ pub(crate) fn complete_postfix( None => return, }; + if !ctx.config.snippets.is_empty() { + add_custom_postfix_completions(acc, ctx, &postfix_snippet, &receiver_text); + } + match try_enum { Some(try_enum) => match try_enum { TryEnum::Result => { @@ -613,4 +613,25 @@ fn main() { r#"fn main() { log::error!("{}", 2+2) }"#, ); } + + #[test] + fn postfix_custom_snippets_completion_for_references() { + check_edit_with_config( + CompletionConfig { + snippets: vec![Snippet::new( + &[], + &["ok".into()], + &["Ok(${receiver})".into()], + "", + &[], + crate::SnippetScope::Expr, + ) + .unwrap()], + ..TEST_CONFIG + }, + "ok", + r#"fn main() { &&42.$0 }"#, + r#"fn main() { Ok(&&42) }"#, + ); + } } diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs index 82b85f2fa5ed..aa5d7e9beb54 100644 --- a/crates/ide-db/src/search.rs +++ b/crates/ide-db/src/search.rs @@ -446,33 +446,47 @@ impl<'a> FindUsages<'a> { }) } - // FIXME: There should be optimization potential here - // Currently we try to descend everything we find which - // means we call `Semantics::descend_into_macros` on - // every textual hit. That function is notoriously - // expensive even for things that do not get down mapped - // into macros. + let find_nodes = move |name: &str, node: &syntax::SyntaxNode, offset: TextSize| { + node.token_at_offset(offset).find(|it| it.text() == name).map(|token| { + // FIXME: There should be optimization potential here + // Currently we try to descend everything we find which + // means we call `Semantics::descend_into_macros` on + // every textual hit. That function is notoriously + // expensive even for things that do not get down mapped + // into macros. + sema.descend_into_macros(token).into_iter().filter_map(|it| it.parent()) + }) + }; + for (text, file_id, search_range) in scope_files(sema, &search_scope) { let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); // Search for occurrences of the items name for offset in match_indices(&text, finder, search_range) { - for name in sema.find_nodes_at_offset_with_descend(&tree, offset) { - if match name { - ast::NameLike::NameRef(name_ref) => self.found_name_ref(&name_ref, sink), - ast::NameLike::Name(name) => self.found_name(&name, sink), - ast::NameLike::Lifetime(lifetime) => self.found_lifetime(&lifetime, sink), - } { - return; + if let Some(iter) = find_nodes(name, &tree, offset) { + for name in iter.filter_map(ast::NameLike::cast) { + if match name { + ast::NameLike::NameRef(name_ref) => { + self.found_name_ref(&name_ref, sink) + } + ast::NameLike::Name(name) => self.found_name(&name, sink), + ast::NameLike::Lifetime(lifetime) => { + self.found_lifetime(&lifetime, sink) + } + } { + return; + } } } } // Search for occurrences of the `Self` referring to our type if let Some((self_ty, finder)) = &include_self_kw_refs { for offset in match_indices(&text, finder, search_range) { - for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { - if self.found_self_ty_name_ref(self_ty, &name_ref, sink) { - return; + if let Some(iter) = find_nodes("Self", &tree, offset) { + for name_ref in iter.filter_map(ast::NameRef::cast) { + if self.found_self_ty_name_ref(self_ty, &name_ref, sink) { + return; + } } } } @@ -493,17 +507,21 @@ impl<'a> FindUsages<'a> { let tree = Lazy::new(move || sema.parse(file_id).syntax().clone()); for offset in match_indices(&text, finder, search_range) { - for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { - if self.found_name_ref(&name_ref, sink) { - return; + if let Some(iter) = find_nodes("super", &tree, offset) { + for name_ref in iter.filter_map(ast::NameRef::cast) { + if self.found_name_ref(&name_ref, sink) { + return; + } } } } if let Some(finder) = &is_crate_root { for offset in match_indices(&text, finder, search_range) { - for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { - if self.found_name_ref(&name_ref, sink) { - return; + if let Some(iter) = find_nodes("crate", &tree, offset) { + for name_ref in iter.filter_map(ast::NameRef::cast) { + if self.found_name_ref(&name_ref, sink) { + return; + } } } } @@ -544,9 +562,11 @@ impl<'a> FindUsages<'a> { let finder = &Finder::new("self"); for offset in match_indices(&text, finder, search_range) { - for name_ref in sema.find_nodes_at_offset_with_descend(&tree, offset) { - if self.found_self_module_name_ref(&name_ref, sink) { - return; + if let Some(iter) = find_nodes("self", &tree, offset) { + for name_ref in iter.filter_map(ast::NameRef::cast) { + if self.found_self_module_name_ref(&name_ref, sink) { + return; + } } } } diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 852a8fd83761..07d117aff10b 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -73,8 +73,8 @@ impl MonikerResult { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct PackageInformation { pub name: String, - pub repo: String, - pub version: String, + pub repo: Option, + pub version: Option, } pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { @@ -256,18 +256,18 @@ pub(crate) fn def_to_moniker( let (name, repo, version) = match krate.origin(db) { CrateOrigin::CratesIo { repo, name } => ( name.unwrap_or(krate.display_name(db)?.canonical_name().to_string()), - repo?, - krate.version(db)?, + repo, + krate.version(db), ), CrateOrigin::Lang(lang) => ( krate.display_name(db)?.canonical_name().to_string(), - "https://github.com/rust-lang/rust/".to_string(), - match lang { + Some("https://github.com/rust-lang/rust/".to_string()), + Some(match lang { LangCrateOrigin::Other => { "https://github.com/rust-lang/rust/library/".into() } lang => format!("https://github.com/rust-lang/rust/library/{lang}",), - }, + }), ), }; PackageInformation { name, repo, version } @@ -315,7 +315,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); check_moniker( @@ -331,7 +331,7 @@ pub mod module { } "#, "foo::module::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -348,7 +348,7 @@ pub mod module { } "#, "foo::module::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -365,7 +365,7 @@ pub mod module { } "#, "foo::module::MyTrait::MY_CONST", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -382,7 +382,7 @@ pub mod module { } "#, "foo::module::MyTrait::MyType", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -405,7 +405,7 @@ pub mod module { } "#, "foo::module::MyStruct::MyTrait::func", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Export, ); } @@ -425,7 +425,7 @@ pub struct St { } "#, "foo::St::a", - r#"PackageInformation { name: "foo", repo: "https://a.b/foo.git", version: "0.1.0" }"#, + r#"PackageInformation { name: "foo", repo: Some("https://a.b/foo.git"), version: Some("0.1.0") }"#, MonikerKind::Import, ); } diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs index fe44856dcad2..b4df0437050f 100644 --- a/crates/ide/src/rename.rs +++ b/crates/ide/src/rename.rs @@ -40,7 +40,9 @@ pub(crate) fn prepare_rename( if def.range_for_rename(&sema).is_none() { bail!("No references found at position") } - let frange = sema.original_range(name_like.syntax()); + let Some(frange) = sema.original_range_opt(name_like.syntax()) else { + bail!("No references found at position"); + }; always!( frange.range.contains_inclusive(position.offset) @@ -51,7 +53,7 @@ pub(crate) fn prepare_rename( .reduce(|acc, cur| match (acc, cur) { // ensure all ranges are the same (Ok(acc_inner), Ok(cur_inner)) if acc_inner == cur_inner => Ok(acc_inner), - (Err(e), _) => Err(e), + (e @ Err(_), _) | (_, e @ Err(_)) => e, _ => bail!("inconsistent text range"), }); @@ -2249,4 +2251,33 @@ fn foo((bar | bar | bar): ()) { "#, ); } + + #[test] + fn regression_13498() { + check( + "Testing", + r" +mod foo { + pub struct Test$0; +} + +use foo::Test as Tester; + +fn main() { + let t = Tester; +} +", + r" +mod foo { + pub struct Testing; +} + +use foo::Testing as Tester; + +fn main() { + let t = Tester; +} +", + ) + } } diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index fedc1a435827..7486b20293a6 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -149,7 +149,7 @@ fn signature_help_for_call( variant.name(db) ); } - hir::CallableKind::Closure | hir::CallableKind::FnPtr => (), + hir::CallableKind::Closure | hir::CallableKind::FnPtr | hir::CallableKind::Other => (), } res.signature.push('('); @@ -189,9 +189,10 @@ fn signature_help_for_call( hir::CallableKind::Function(func) if callable.return_type().contains_unknown() => { render(func.ret_type(db)) } - hir::CallableKind::Function(_) | hir::CallableKind::Closure | hir::CallableKind::FnPtr => { - render(callable.return_type()) - } + hir::CallableKind::Function(_) + | hir::CallableKind::Closure + | hir::CallableKind::FnPtr + | hir::CallableKind::Other => render(callable.return_type()), hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_) => {} } Some(res) @@ -387,10 +388,9 @@ mod tests { } fn check(ra_fixture: &str, expect: Expect) { - // Implicitly add `Sized` to avoid noisy `T: ?Sized` in the results. let fixture = format!( r#" -#[lang = "sized"] trait Sized {{}} +//- minicore: sized, fn {ra_fixture} "# ); @@ -1331,4 +1331,19 @@ fn f() { "#]], ); } + + #[test] + fn help_for_generic_call() { + check( + r#" +fn f i32>(f: F) { + f($0) +} +"#, + expect![[r#" + (u8, u16) -> i32 + ^^ --- + "#]], + ); + } } diff --git a/crates/rust-analyzer/src/cli/lsif.rs b/crates/rust-analyzer/src/cli/lsif.rs index 5ff347b9bd72..c74ddabb1777 100644 --- a/crates/rust-analyzer/src/cli/lsif.rs +++ b/crates/rust-analyzer/src/cli/lsif.rs @@ -106,12 +106,12 @@ impl LsifManager<'_> { manager: "cargo".to_string(), uri: None, content: None, - repository: Some(lsif::Repository { - url: pi.repo, + repository: pi.repo.map(|url| lsif::Repository { + url, r#type: "git".to_string(), commit_id: None, }), - version: Some(pi.version), + version: pi.version, })); self.package_map.insert(package_information, result_set_id); result_set_id diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index 16298862b50f..ca7ba896b67c 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -231,7 +231,7 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { package: Some(scip_types::Package { manager: "cargo".to_string(), name: package_name, - version, + version: version.unwrap_or_else(|| ".".to_string()), ..Default::default() }) .into(), @@ -415,4 +415,42 @@ pub mod module { "", ); } + + #[test] + fn global_symbol_for_pub_struct() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar { i: 0 }; + } + //- /foo.rs + pub struct Bar$0 { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } + + #[test] + fn global_symbol_for_pub_struct_reference() { + check_symbol( + r#" + //- /lib.rs crate:main + mod foo; + + fn main() { + let _bar = foo::Bar$0 { i: 0 }; + } + //- /foo.rs + pub struct Bar { + pub i: i32, + } + "#, + "rust-analyzer cargo main . foo/Bar#", + ); + } } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 1ed8f2bb5f35..4072ae585dbd 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -56,6 +56,9 @@ mod patch_old_style; // parsing the old name. config_data! { struct ConfigData { + /// Whether to insert #[must_use] when generating `as_` methods + /// for enum variants. + assist_emitMustUse: bool = "false", /// Placeholder expression to use for missing expressions in assists. assist_expressionFillDefault: ExprFillDefaultDef = "\"todo\"", @@ -1276,6 +1279,7 @@ impl Config { allowed: None, insert_use: self.insert_use_config(), prefer_no_std: self.data.imports_prefer_no_std, + assist_emit_must_use: self.data.assist_emitMustUse, } } diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs index 4057a75e7c1e..8c26009add2b 100644 --- a/crates/syntax/src/ast/make.rs +++ b/crates/syntax/src/ast/make.rs @@ -334,6 +334,10 @@ pub fn block_expr( ast_from_text(&format!("fn f() {buf}")) } +pub fn tail_only_block_expr(tail_expr: ast::Expr) -> ast::BlockExpr { + ast_from_text(&format!("fn f() {{ {tail_expr} }}")) +} + /// Ideally this function wouldn't exist since it involves manual indenting. /// It differs from `make::block_expr` by also supporting comments. /// @@ -656,6 +660,22 @@ pub fn let_stmt( }; ast_from_text(&format!("fn f() {{ {text} }}")) } + +pub fn let_else_stmt( + pattern: ast::Pat, + ty: Option, + expr: ast::Expr, + diverging: ast::BlockExpr, +) -> ast::LetStmt { + let mut text = String::new(); + format_to!(text, "let {pattern}"); + if let Some(ty) = ty { + format_to!(text, ": {ty}"); + } + format_to!(text, " = {expr} else {diverging};"); + ast_from_text(&format!("fn f() {{ {text} }}")) +} + pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt { let semi = if expr.is_block_like() { "" } else { ";" }; ast_from_text(&format!("fn f() {{ {expr}{semi} (); }}")) diff --git a/docs/dev/guide.md b/docs/dev/guide.md index 52a13da31c5d..56a68ef04379 100644 --- a/docs/dev/guide.md +++ b/docs/dev/guide.md @@ -338,7 +338,7 @@ The algorithm for building a tree of modules is to start with a crate root declarations and recursively process child modules. This is handled by the [`module_tree_query`], with two slight variations. -[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L116-L123 +[`module_tree_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L115-L133 First, rust-analyzer builds a module tree for all crates in a source root simultaneously. The main reason for this is historical (`module_tree` predates @@ -361,7 +361,7 @@ the same, we don't have to re-execute [`module_tree_query`]. In fact, we only need to re-execute it when we add/remove new files or when we change mod declarations. -[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/module_tree.rs#L41 +[`submodules_query`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/module_tree.rs#L41 We store the resulting modules in a `Vec`-based indexed arena. The indices in the arena becomes module IDs. And this brings us to the next topic: @@ -389,8 +389,8 @@ integers which can "intern" a location and return an integer ID back. The salsa database we use includes a couple of [interners]. How to "garbage collect" unused locations is an open question. -[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/base_db/src/loc2id.rs#L65-L71 -[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/db.rs#L22-L23 +[`LocationInterner`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_db/src/loc2id.rs#L65-L71 +[interners]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/db.rs#L22-L23 For example, we use `LocationInterner` to assign IDs to definitions of functions, structs, enums, etc. The location, [`DefLoc`] contains two bits of information: @@ -404,7 +404,7 @@ using offsets, text ranges or syntax trees as keys and values for queries. What we do instead is we store "index" of the item among all of the items of a file (so, a positional based ID, but localized to a single file). -[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L127-L139 +[`DefLoc`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L129-L139 One thing we've glossed over for the time being is support for macros. We have only proof of concept handling of macros at the moment, but they are extremely @@ -437,7 +437,7 @@ terms of `HirFileId`! This does not recur infinitely though: any chain of `HirFileId`s bottoms out in `HirFileId::FileId`, that is, some source file actually written by the user. -[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ids.rs#L18-L125 +[`HirFileId`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ids.rs#L31-L93 Now that we understand how to identify a definition, in a source or in a macro-generated file, we can discuss name resolution a bit. @@ -451,14 +451,13 @@ each module into a position-independent representation which does not change if we modify bodies of the items. After that we [loop] resolving all imports until we've reached a fixed point. -[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L113-L117 -[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres.rs#L186-L196 - +[lower]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L113-L147 +[loop]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres.rs#L186-L196 And, given all our preparation with IDs and a position-independent representation, it is satisfying to [test] that typing inside function body does not invalidate name resolution results. -[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/tests.rs#L376 +[test]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/tests.rs#L376 An interesting fact about name resolution is that it "erases" all of the intermediate paths from the imports: in the end, we know which items are defined @@ -493,10 +492,10 @@ there's an intermediate [projection query] which returns only the first position-independent part of the lowering. The result of this query is stable. Naturally, name resolution [uses] this stable projection query. -[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59 -[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L52-L59 -[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/nameres/lower.rs#L97-L103 -[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/query_definitions.rs#L49 +[imports]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 +[`SourceMap`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L52-L59 +[projection query]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/nameres/lower.rs#L97-L103 +[uses]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/query_definitions.rs#L49 ## Type inference @@ -518,10 +517,10 @@ construct a mapping from `ExprId`s to types. [@flodiebold]: https://github.com/flodiebold [#327]: https://github.com/rust-lang/rust-analyzer/pull/327 -[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs -[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L13-L15 -[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/expr.rs#L41-L44 -[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/hir/src/ty.rs#L1208-L1223 +[lower the AST]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs +[positional ID]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L13-L15 +[a source map]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/expr.rs#L41-L44 +[type inference]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_hir/src/ty.rs#L1208-L1223 ## Tying it all together: completion @@ -563,10 +562,11 @@ the type to completion. [catch]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_lsp_server/src/main_loop.rs#L436-L442 [the handler]: https://salsa.zulipchat.com/#narrow/stream/181542-rfcs.2Fsalsa-query-group/topic/design.20next.20steps [ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/lib.rs#L439-L444 -[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L46-L62 -[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L14-L37 -["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L72-L75 -[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L116-L120 -[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/completion_context.rs#L123 -[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion.rs#L52-L59 -[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ide_api/src/completion/complete_dot.rs#L6-L22 +[ask analysis for completion]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/lib.rs#L439-L444 +[completion implementation]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L46-L62 +[`CompletionContext`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L14-L37 +["IntelliJ Trick"]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L72-L75 +[find an ancestor `fn` node]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L116-L120 +[semantic model]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/completion_context.rs#L123 +[series of independent completion routines]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion.rs#L52-L59 +[`complete_dot`]: https://github.com/rust-lang/rust-analyzer/blob/guide-2019-01/crates/ra_ide_api/src/completion/complete_dot.rs#L6-L22 diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 502833de72c1..36794efe4272 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -1,3 +1,9 @@ +[[rust-analyzer.assist.emitMustUse]]rust-analyzer.assist.emitMustUse (default: `false`):: ++ +-- +Whether to insert #[must_use] when generating `as_` methods +for enum variants. +-- [[rust-analyzer.assist.expressionFillDefault]]rust-analyzer.assist.expressionFillDefault (default: `"todo"`):: + -- diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index c30838e5f5e1..49500e390a50 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -487,6 +487,12 @@ https://docs.helix-editor.com/[Helix] supports LSP by default. However, it won't install `rust-analyzer` automatically. You can follow instructions for installing <>. +=== Crates + +There is a package named `ra_ap_rust_analyzer` available on https://crates.io/crates/ra_ap_rust-analyzer[crates.io], for someone who wants to use it programmatically. + +For more details, see https://github.com/rust-lang/rust-analyzer/blob/master/.github/workflows/publish.yml[the publish workflow]. + == Troubleshooting Start with looking at the rust-analyzer version. diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index a72865d4fe44..0b25564e28d3 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -23,7 +23,7 @@ "esbuild": "^0.14.48", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", - "ovsx": "^0.5.1", + "ovsx": "^0.5.2", "prettier": "^2.7.1", "tslib": "^2.4.0", "typescript": "^4.7.4", @@ -2874,9 +2874,9 @@ } }, "node_modules/ovsx": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz", - "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz", + "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==", "dev": true, "dependencies": { "commander": "^6.1.0", @@ -5958,9 +5958,9 @@ } }, "ovsx": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.1.tgz", - "integrity": "sha512-3OWq0l7DuVHi2bd2aQe5+QVQlFIqvrcw3/2vGXL404L6Tr+R4QHtzfnYYghv8CCa85xJHjU0RhcaC7pyXkAUbg==", + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/ovsx/-/ovsx-0.5.2.tgz", + "integrity": "sha512-UbLultRCk46WddeA0Cly4hoRhzBJUiLgbIEViXlgOvV54LbsppClDkMLoCevUUBHoiNdMX2NuiSgURAEXgCZdw==", "dev": true, "requires": { "commander": "^6.1.0", diff --git a/editors/code/package.json b/editors/code/package.json index 6771cad28a79..1a97a9c08937 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -49,7 +49,7 @@ "esbuild": "^0.14.48", "eslint": "^8.19.0", "eslint-config-prettier": "^8.5.0", - "ovsx": "^0.5.1", + "ovsx": "^0.5.2", "prettier": "^2.7.1", "tslib": "^2.4.0", "typescript": "^4.7.4", @@ -100,22 +100,32 @@ { "command": "rust-analyzer.syntaxTree", "title": "Show Syntax Tree", - "category": "rust-analyzer" + "category": "rust-analyzer (debug command)" }, { "command": "rust-analyzer.viewHir", "title": "View Hir", - "category": "rust-analyzer" + "category": "rust-analyzer (debug command)" }, { "command": "rust-analyzer.viewFileText", "title": "View File Text (as seen by the server)", - "category": "rust-analyzer" + "category": "rust-analyzer (debug command)" }, { "command": "rust-analyzer.viewItemTree", "title": "Debug ItemTree", - "category": "rust-analyzer" + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.shuffleCrateGraph", + "title": "Shuffle Crate Graph", + "category": "rust-analyzer (debug command)" + }, + { + "command": "rust-analyzer.memoryUsage", + "title": "Memory Usage (Clears Database)", + "category": "rust-analyzer (debug command)" }, { "command": "rust-analyzer.viewCrateGraph", @@ -172,16 +182,6 @@ "title": "Status", "category": "rust-analyzer" }, - { - "command": "rust-analyzer.memoryUsage", - "title": "Memory Usage (Clears Database)", - "category": "rust-analyzer" - }, - { - "command": "rust-analyzer.shuffleCrateGraph", - "title": "Shuffle Crate Graph", - "category": "rust-analyzer" - }, { "command": "rust-analyzer.reloadWorkspace", "title": "Reload workspace", @@ -397,6 +397,11 @@ "type": "boolean" }, "$generated-start": {}, + "rust-analyzer.assist.emitMustUse": { + "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", + "default": false, + "type": "boolean" + }, "rust-analyzer.assist.expressionFillDefault": { "markdownDescription": "Placeholder expression to use for missing expressions in assists.", "default": "todo", diff --git a/triagebot.toml b/triagebot.toml index fa0824ac53c0..a910e012b734 100644 --- a/triagebot.toml +++ b/triagebot.toml @@ -1 +1,11 @@ [assign] + +[shortcut] + +[relabel] +allow-unauthenticated = [ + "S-*", +] + +[autolabel."S-waiting-on-review"] +new_pr = true From a2a1d995458c60175ad3aa37b7b8c5b2752e968c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Wed, 23 Nov 2022 17:24:03 +0200 Subject: [PATCH 22/22] :arrow_up: rust-analyzer --- Cargo.lock | 116 +++++--- crates/base-db/Cargo.toml | 2 +- crates/cfg/Cargo.toml | 2 +- crates/flycheck/Cargo.toml | 3 +- crates/flycheck/src/lib.rs | 27 +- crates/hir-def/Cargo.toml | 2 +- crates/hir-def/src/data.rs | 18 +- crates/hir-def/src/item_tree.rs | 3 +- crates/hir-def/src/item_tree/lower.rs | 10 +- crates/hir-def/src/item_tree/pretty.rs | 19 +- .../src/macro_expansion_tests/mbe/matching.rs | 10 +- .../src/macro_expansion_tests/proc_macros.rs | 6 +- crates/hir-def/src/nameres/collector.rs | 4 + crates/hir-def/src/nameres/path_resolution.rs | 7 + crates/hir-def/src/nameres/tests.rs | 6 +- crates/hir-def/src/nameres/tests/globs.rs | 33 ++- .../src/nameres/tests/mod_resolution.rs | 2 +- crates/hir-expand/Cargo.toml | 2 +- crates/hir-expand/src/fixup.rs | 96 +++++-- crates/hir-expand/src/lib.rs | 4 +- crates/hir-ty/Cargo.toml | 2 +- crates/hir-ty/src/lib.rs | 50 ++-- crates/hir-ty/src/method_resolution.rs | 2 +- crates/hir-ty/src/tests/method_resolution.rs | 40 +-- crates/hir-ty/src/tests/regression.rs | 16 ++ crates/hir-ty/src/tests/simple.rs | 18 +- crates/hir-ty/src/tests/traits.rs | 36 +-- crates/hir/Cargo.toml | 2 +- crates/hir/src/lib.rs | 27 +- crates/hir/src/semantics.rs | 37 ++- crates/hir/src/source_analyzer.rs | 16 +- crates/ide-assists/Cargo.toml | 2 +- .../src/handlers/add_missing_impl_members.rs | 38 +-- .../src/handlers/extract_function.rs | 270 +++++++++++++++++- .../src/handlers/fix_visibility.rs | 20 +- .../src/handlers/generate_enum_variant.rs | 12 +- .../src/handlers/generate_function.rs | 4 +- .../src/handlers/move_format_string_arg.rs | 6 +- crates/ide-assists/src/handlers/remove_dbg.rs | 63 +++- .../replace_derive_with_manual_impl.rs | 2 - crates/ide-assists/src/tests/generated.rs | 4 +- crates/ide-assists/src/utils.rs | 4 + crates/ide-completion/Cargo.toml | 2 +- .../src/completions/item_list/trait_impl.rs | 159 +++++++++-- crates/ide-completion/src/context/analysis.rs | 8 +- crates/ide-completion/src/tests/item_list.rs | 32 +++ crates/ide-db/Cargo.toml | 2 +- crates/ide-db/src/line_index.rs | 7 +- .../src/syntax_helpers/format_string_exprs.rs | 60 ++-- crates/ide-diagnostics/Cargo.toml | 2 +- .../src/handlers/macro_error.rs | 5 +- .../src/handlers/no_such_field.rs | 4 +- .../src/handlers/unresolved_macro_call.rs | 5 +- .../src/handlers/unresolved_proc_macro.rs | 12 +- .../src/handlers/useless_braces.rs | 20 +- crates/ide-diagnostics/src/lib.rs | 22 ++ crates/ide-ssr/Cargo.toml | 2 +- crates/ide/Cargo.toml | 2 +- crates/ide/src/goto_definition.rs | 6 +- crates/ide/src/hover.rs | 9 +- crates/ide/src/inlay_hints.rs | 243 +++++++++++----- crates/ide/src/lib.rs | 6 +- crates/ide/src/moniker.rs | 20 +- crates/ide/src/references.rs | 2 + crates/ide/src/signature_help.rs | 31 ++ crates/ide/src/static_index.rs | 7 +- crates/limit/Cargo.toml | 2 +- crates/mbe/Cargo.toml | 2 +- crates/mbe/src/syntax_bridge.rs | 89 ++++-- crates/mbe/src/syntax_bridge/tests.rs | 93 ++++++ crates/parser/Cargo.toml | 2 +- crates/paths/Cargo.toml | 2 +- crates/proc-macro-api/Cargo.toml | 2 +- crates/proc-macro-srv-cli/Cargo.toml | 2 +- crates/proc-macro-srv/Cargo.toml | 2 +- crates/proc-macro-srv/src/abis/mod.rs | 2 +- crates/proc-macro-srv/src/dylib.rs | 4 +- crates/proc-macro-srv/src/lib.rs | 4 +- crates/proc-macro-srv/src/tests/mod.rs | 6 +- crates/proc-macro-test/Cargo.toml | 2 +- crates/proc-macro-test/imp/Cargo.toml | 2 +- crates/profile/Cargo.toml | 2 +- crates/project-model/Cargo.toml | 2 +- crates/project-model/src/build_scripts.rs | 2 +- crates/project-model/src/cargo_workspace.rs | 50 +++- crates/project-model/src/sysroot.rs | 27 +- crates/project-model/src/tests.rs | 71 +---- crates/project-model/src/workspace.rs | 84 ++++-- crates/rust-analyzer/Cargo.toml | 2 +- crates/rust-analyzer/src/cargo_target_spec.rs | 10 +- crates/rust-analyzer/src/cli/load_cargo.rs | 22 +- crates/rust-analyzer/src/cli/scip.rs | 115 +++++--- crates/rust-analyzer/src/config.rs | 83 +++++- .../rust-analyzer/src/diagnostics/to_proto.rs | 22 +- crates/rust-analyzer/src/from_proto.rs | 6 +- crates/rust-analyzer/src/global_state.rs | 2 +- crates/rust-analyzer/src/handlers.rs | 19 +- crates/rust-analyzer/src/line_index.rs | 60 +++- crates/rust-analyzer/src/lsp_utils.rs | 111 +++---- crates/rust-analyzer/src/main_loop.rs | 8 +- crates/rust-analyzer/src/mem_docs.rs | 8 +- crates/rust-analyzer/src/reload.rs | 64 ++--- crates/rust-analyzer/src/to_proto.rs | 60 ++-- crates/sourcegen/Cargo.toml | 2 +- crates/stdx/Cargo.toml | 4 +- crates/syntax/Cargo.toml | 2 +- crates/syntax/fuzz/Cargo.toml | 2 +- crates/syntax/rust.ungram | 7 +- crates/syntax/src/ast/generated/nodes.rs | 2 + crates/syntax/src/ast/token_ext.rs | 46 ++- crates/syntax/src/tests/sourcegen_ast.rs | 2 +- crates/test-utils/Cargo.toml | 2 +- crates/text-edit/Cargo.toml | 2 +- crates/toolchain/Cargo.toml | 2 +- crates/tt/Cargo.toml | 2 +- crates/vfs-notify/Cargo.toml | 2 +- crates/vfs/Cargo.toml | 2 +- docs/dev/architecture.md | 6 +- docs/user/generated_config.adoc | 20 +- docs/user/manual.adoc | 2 +- editors/code/package.json | 46 ++- editors/code/src/client.ts | 42 ++- editors/code/src/config.ts | 3 + editors/code/src/ctx.ts | 3 +- editors/code/src/main.ts | 24 ++ xtask/Cargo.toml | 2 +- 126 files changed, 2089 insertions(+), 895 deletions(-) create mode 100644 crates/mbe/src/syntax_bridge/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 8931c17bbdc1..41c5d36671de 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -221,6 +221,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "command-group" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7a8a86f409b4a59df3a3e4bee2de0b83f1755fdd2a25e3a9684c396fc4bed2c" +dependencies = [ + "nix", + "winapi", +] + [[package]] name = "countme" version = "3.0.1" @@ -300,7 +310,7 @@ dependencies = [ "hashbrown", "lock_api", "once_cell", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.4", ] [[package]] @@ -359,14 +369,14 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "4b9663d381d07ae25dc88dbdf27df458faa83a9b25336bcac83d5e452b5fc9d3" dependencies = [ "cfg-if", "libc", "redox_syscall", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -390,6 +400,7 @@ name = "flycheck" version = "0.0.0" dependencies = [ "cargo_metadata", + "command-group", "crossbeam-channel", "jod-thread", "paths", @@ -963,11 +974,24 @@ dependencies = [ [[package]] name = "miow" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7377f7792b3afb6a3cba68daa54ca23c032137010460d667fda53a8d66be00e" +checksum = "52ffbca2f655e33c08be35d87278e5b18b89550a37dbd598c20db92f6a471123" dependencies = [ - "windows-sys 0.28.0", + "windows-sys 0.42.0", +] + +[[package]] +name = "nix" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", ] [[package]] @@ -1037,7 +1061,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.4", ] [[package]] @@ -1056,15 +1080,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys 0.36.1", + "windows-sys 0.42.0", ] [[package]] @@ -1979,19 +2003,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" -dependencies = [ - "windows_aarch64_msvc 0.28.0", - "windows_i686_gnu 0.28.0", - "windows_i686_msvc 0.28.0", - "windows_x86_64_gnu 0.28.0", - "windows_x86_64_msvc 0.28.0", -] - [[package]] name = "windows-sys" version = "0.36.1" @@ -2006,10 +2017,25 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_msvc" -version = "0.28.0" +name = "windows-sys" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.0", + "windows_i686_gnu 0.42.0", + "windows_i686_msvc 0.42.0", + "windows_x86_64_gnu 0.42.0", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" +checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" [[package]] name = "windows_aarch64_msvc" @@ -2018,10 +2044,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" [[package]] -name = "windows_i686_gnu" -version = "0.28.0" +name = "windows_aarch64_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" +checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" [[package]] name = "windows_i686_gnu" @@ -2030,10 +2056,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" [[package]] -name = "windows_i686_msvc" -version = "0.28.0" +name = "windows_i686_gnu" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" +checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" [[package]] name = "windows_i686_msvc" @@ -2042,10 +2068,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" [[package]] -name = "windows_x86_64_gnu" -version = "0.28.0" +name = "windows_i686_msvc" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" +checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" [[package]] name = "windows_x86_64_gnu" @@ -2054,10 +2080,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" [[package]] -name = "windows_x86_64_msvc" -version = "0.28.0" +name = "windows_x86_64_gnu" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" +checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" [[package]] name = "windows_x86_64_msvc" @@ -2065,6 +2097,12 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" + [[package]] name = "write-json" version = "0.1.2" diff --git a/crates/base-db/Cargo.toml b/crates/base-db/Cargo.toml index f02a51ab6c47..a484ecec6825 100644 --- a/crates/base-db/Cargo.toml +++ b/crates/base-db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/cfg/Cargo.toml b/crates/cfg/Cargo.toml index ee1ad677a95f..2857420c285a 100644 --- a/crates/cfg/Cargo.toml +++ b/crates/cfg/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/flycheck/Cargo.toml b/crates/flycheck/Cargo.toml index 2ad32d24837d..514d567fcce7 100644 --- a/crates/flycheck/Cargo.toml +++ b/crates/flycheck/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false @@ -17,6 +17,7 @@ rustc-hash = "1.1.0" serde = { version = "1.0.137", features = ["derive"] } serde_json = "1.0.86" jod-thread = "0.1.2" +command-group = "1.0.8" toolchain = { path = "../toolchain", version = "0.0.0" } stdx = { path = "../stdx", version = "0.0.0" } diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 8a91d6066614..8f93dad06e3f 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -10,11 +10,12 @@ use std::{ time::Duration, }; +use command_group::{CommandGroup, GroupChild}; use crossbeam_channel::{never, select, unbounded, Receiver, Sender}; use paths::AbsPathBuf; use rustc_hash::FxHashMap; use serde::Deserialize; -use stdx::{process::streaming_output, JodChild}; +use stdx::process::streaming_output; pub use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, @@ -39,7 +40,7 @@ pub enum InvocationLocation { pub enum FlycheckConfig { CargoCommand { command: String, - target_triple: Option, + target_triples: Vec, all_targets: bool, no_default_features: bool, all_features: bool, @@ -285,7 +286,7 @@ impl FlycheckActor { let (mut cmd, args) = match &self.config { FlycheckConfig::CargoCommand { command, - target_triple, + target_triples, no_default_features, all_targets, all_features, @@ -299,7 +300,7 @@ impl FlycheckActor { cmd.args(&["--workspace", "--message-format=json", "--manifest-path"]) .arg(self.root.join("Cargo.toml").as_os_str()); - if let Some(target) = target_triple { + for target in target_triples { cmd.args(&["--target", target.as_str()]); } if *all_targets { @@ -359,10 +360,12 @@ impl FlycheckActor { } } +struct JodChild(GroupChild); + /// A handle to a cargo process used for fly-checking. struct CargoHandle { /// The handle to the actual cargo process. As we cannot cancel directly from with - /// a read syscall dropping and therefor terminating the process is our best option. + /// a read syscall dropping and therefore terminating the process is our best option. child: JodChild, thread: jod_thread::JoinHandle>, receiver: Receiver, @@ -371,10 +374,10 @@ struct CargoHandle { impl CargoHandle { fn spawn(mut command: Command) -> std::io::Result { command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null()); - let mut child = JodChild::spawn(command)?; + let mut child = command.group_spawn().map(JodChild)?; - let stdout = child.stdout.take().unwrap(); - let stderr = child.stderr.take().unwrap(); + let stdout = child.0.inner().stdout.take().unwrap(); + let stderr = child.0.inner().stderr.take().unwrap(); let (sender, receiver) = unbounded(); let actor = CargoActor::new(sender, stdout, stderr); @@ -386,13 +389,13 @@ impl CargoHandle { } fn cancel(mut self) { - let _ = self.child.kill(); - let _ = self.child.wait(); + let _ = self.child.0.kill(); + let _ = self.child.0.wait(); } fn join(mut self) -> io::Result<()> { - let _ = self.child.kill(); - let exit_status = self.child.wait()?; + let _ = self.child.0.kill(); + let exit_status = self.child.0.wait()?; let (read_at_least_one_message, error) = self.thread.join()?; if read_at_least_one_message || exit_status.success() { Ok(()) diff --git a/crates/hir-def/Cargo.toml b/crates/hir-def/Cargo.toml index 4ad8e75970b5..22f98ea7cd45 100644 --- a/crates/hir-def/Cargo.toml +++ b/crates/hir-def/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/hir-def/src/data.rs b/crates/hir-def/src/data.rs index 2dc69b00ace0..9c7696908648 100644 --- a/crates/hir-def/src/data.rs +++ b/crates/hir-def/src/data.rs @@ -236,11 +236,19 @@ impl TraitData { .by_key("rustc_skip_array_during_method_dispatch") .exists(); - let mut collector = - AssocItemCollector::new(db, module_id, tree_id.file_id(), ItemContainerId::TraitId(tr)); - collector.collect(&item_tree, tree_id.tree_id(), &tr_def.items); - let (items, attribute_calls, diagnostics) = collector.finish(); - + let (items, attribute_calls, diagnostics) = match &tr_def.items { + Some(items) => { + let mut collector = AssocItemCollector::new( + db, + module_id, + tree_id.file_id(), + ItemContainerId::TraitId(tr), + ); + collector.collect(&item_tree, tree_id.tree_id(), items); + collector.finish() + } + None => Default::default(), + }; ( Arc::new(TraitData { name, diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs index 570344596def..0aa531eff71f 100644 --- a/crates/hir-def/src/item_tree.rs +++ b/crates/hir-def/src/item_tree.rs @@ -666,7 +666,8 @@ pub struct Trait { pub generic_params: Interned, pub is_auto: bool, pub is_unsafe: bool, - pub items: Box<[AssocItem]>, + /// This is [`None`] if this Trait is a trait alias. + pub items: Option>, pub ast_id: FileAstId, } diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs index 79249757d9e9..b25274bccc9a 100644 --- a/crates/hir-def/src/item_tree/lower.rs +++ b/crates/hir-def/src/item_tree/lower.rs @@ -451,15 +451,7 @@ impl<'a> Ctx<'a> { .collect() }); let ast_id = self.source_ast_id_map.ast_id(trait_def); - let res = Trait { - name, - visibility, - generic_params, - is_auto, - is_unsafe, - items: items.unwrap_or_default(), - ast_id, - }; + let res = Trait { name, visibility, generic_params, is_auto, is_unsafe, items, ast_id }; Some(id(self.data().traits.alloc(res))) } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index da1643152c2f..48c40df22ff5 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -375,12 +375,21 @@ impl<'a> Printer<'a> { } w!(self, "trait {}", name); self.print_generic_params(generic_params); - self.print_where_clause_and_opening_brace(generic_params); - self.indented(|this| { - for item in &**items { - this.print_mod_item((*item).into()); + match items { + Some(items) => { + self.print_where_clause_and_opening_brace(generic_params); + self.indented(|this| { + for item in &**items { + this.print_mod_item((*item).into()); + } + }); } - }); + None => { + w!(self, " = "); + // FIXME: Print the aliased traits + self.print_where_clause_and_opening_brace(generic_params); + } + } wln!(self, "}}"); } ModItem::Impl(it) => { diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs index bc162d0fa206..fc90c6e9f370 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs @@ -94,11 +94,11 @@ macro_rules! m { ($($s:stmt)*) => (stringify!($($s |)*);) } stringify!(; -|; -|92|; -|let x = 92|; +| ; +|92| ; +|let x = 92| ; |loop {} -|; +| ; |); "#]], ); @@ -118,7 +118,7 @@ m!(.. .. ..); macro_rules! m { ($($p:pat)*) => (stringify!($($p |)*);) } -stringify!(.. .. ..|); +stringify!(.. .. .. |); "#]], ); } 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 029821e5e87f..118c14ed843f 100644 --- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs +++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs @@ -82,14 +82,14 @@ fn attribute_macro_syntax_completion_2() { #[proc_macros::identity_when_valid] fn foo() { bar.; blub } "#, - expect![[r##" + expect![[r#" #[proc_macros::identity_when_valid] fn foo() { bar.; blub } fn foo() { - bar.; + bar. ; blub -}"##]], +}"#]], ); } diff --git a/crates/hir-def/src/nameres/collector.rs b/crates/hir-def/src/nameres/collector.rs index 9ffc218818ca..b0dd01f9dbea 100644 --- a/crates/hir-def/src/nameres/collector.rs +++ b/crates/hir-def/src/nameres/collector.rs @@ -212,6 +212,7 @@ impl Import { #[derive(Debug, Eq, PartialEq)] struct ImportDirective { + /// The module this import directive is in. module_id: LocalModuleId, import: Import, status: PartialResolvedImport, @@ -963,8 +964,10 @@ impl DefCollector<'_> { fn update( &mut self, + // The module for which `resolutions` have been resolve module_id: LocalModuleId, resolutions: &[(Option, PerNs)], + // Visibility this import will have vis: Visibility, import_type: ImportType, ) { @@ -974,6 +977,7 @@ impl DefCollector<'_> { fn update_recursive( &mut self, + // The module for which `resolutions` have been resolve module_id: LocalModuleId, resolutions: &[(Option, PerNs)], // All resolutions are imported with this visibility; the visibilities in diff --git a/crates/hir-def/src/nameres/path_resolution.rs b/crates/hir-def/src/nameres/path_resolution.rs index 8dfda6df64e7..20d39ec6cb92 100644 --- a/crates/hir-def/src/nameres/path_resolution.rs +++ b/crates/hir-def/src/nameres/path_resolution.rs @@ -73,7 +73,10 @@ impl DefMap { pub(crate) fn resolve_visibility( &self, db: &dyn DefDatabase, + // module to import to original_module: LocalModuleId, + // pub(path) + // ^^^^ this visibility: &RawVisibility, ) -> Option { let mut vis = match visibility { @@ -115,6 +118,7 @@ impl DefMap { &self, db: &dyn DefDatabase, mode: ResolveMode, + // module to import to mut original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, @@ -361,6 +365,9 @@ impl DefMap { ); } }; + + curr_per_ns = curr_per_ns + .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); } ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate)) diff --git a/crates/hir-def/src/nameres/tests.rs b/crates/hir-def/src/nameres/tests.rs index 70dd2eb3ade6..0d90047c28f6 100644 --- a/crates/hir-def/src/nameres/tests.rs +++ b/crates/hir-def/src/nameres/tests.rs @@ -58,9 +58,9 @@ extern { "#, expect![[r#" crate - E: t + E: _ S: t v - V: t v + V: _ foo: t crate::foo @@ -307,7 +307,7 @@ pub struct FromLib; Bar: t v crate::foo - Bar: t v + Bar: _ FromLib: t v "#]], ); diff --git a/crates/hir-def/src/nameres/tests/globs.rs b/crates/hir-def/src/nameres/tests/globs.rs index b2a6a592cf38..88a3c76393f0 100644 --- a/crates/hir-def/src/nameres/tests/globs.rs +++ b/crates/hir-def/src/nameres/tests/globs.rs @@ -119,7 +119,7 @@ use foo::*; use foo::bar::*; //- /foo/mod.rs -mod bar; +pub mod bar; fn Foo() {}; pub struct Foo {}; @@ -132,6 +132,7 @@ pub(crate) struct PubCrateStruct; crate Foo: t PubCrateStruct: t v + bar: t foo: t crate::foo @@ -336,3 +337,33 @@ mod d { "#]], ); } + +#[test] +fn glob_name_collision_check_visibility() { + check( + r#" +mod event { + mod serenity { + pub fn Event() {} + } + use serenity::*; + + pub struct Event {} +} + +use event::Event; + "#, + expect![[r#" + crate + Event: t + event: t + + crate::event + Event: t v + serenity: t + + crate::event::serenity + Event: v + "#]], + ); +} diff --git a/crates/hir-def/src/nameres/tests/mod_resolution.rs b/crates/hir-def/src/nameres/tests/mod_resolution.rs index ba3bf8b5a5cf..c575bf7cac25 100644 --- a/crates/hir-def/src/nameres/tests/mod_resolution.rs +++ b/crates/hir-def/src/nameres/tests/mod_resolution.rs @@ -580,7 +580,7 @@ fn module_resolution_decl_inside_inline_module_in_crate_root() { //- /main.rs mod foo { #[path = "baz.rs"] - mod bar; + pub mod bar; } use self::foo::bar::Baz; diff --git a/crates/hir-expand/Cargo.toml b/crates/hir-expand/Cargo.toml index 3359c99b3961..77eb1fd45043 100644 --- a/crates/hir-expand/Cargo.toml +++ b/crates/hir-expand/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/hir-expand/src/fixup.rs b/crates/hir-expand/src/fixup.rs index 893e6fe4b824..a4abe75626e6 100644 --- a/crates/hir-expand/src/fixup.rs +++ b/crates/hir-expand/src/fixup.rs @@ -4,6 +4,7 @@ use std::mem; use mbe::{SyntheticToken, SyntheticTokenId, TokenMap}; use rustc_hash::FxHashMap; +use smallvec::SmallVec; use syntax::{ ast::{self, AstNode, HasLoopBody}, match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, @@ -292,25 +293,34 @@ pub(crate) fn reverse_fixups( token_map: &TokenMap, undo_info: &SyntaxFixupUndoInfo, ) { - tt.token_trees.retain(|tt| match tt { - tt::TokenTree::Leaf(leaf) => { - token_map.synthetic_token_id(leaf.id()).is_none() - || token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID) - } - tt::TokenTree::Subtree(st) => st.delimiter.map_or(true, |d| { - token_map.synthetic_token_id(d.id).is_none() - || token_map.synthetic_token_id(d.id) != Some(EMPTY_ID) - }), - }); - tt.token_trees.iter_mut().for_each(|tt| match tt { - tt::TokenTree::Subtree(tt) => reverse_fixups(tt, token_map, undo_info), - tt::TokenTree::Leaf(leaf) => { - if let Some(id) = token_map.synthetic_token_id(leaf.id()) { - let original = &undo_info.original[id.0 as usize]; - *tt = tt::TokenTree::Subtree(original.clone()); + let tts = std::mem::take(&mut tt.token_trees); + tt.token_trees = tts + .into_iter() + .filter(|tt| match tt { + tt::TokenTree::Leaf(leaf) => token_map.synthetic_token_id(leaf.id()) != Some(EMPTY_ID), + tt::TokenTree::Subtree(st) => { + st.delimiter.map_or(true, |d| token_map.synthetic_token_id(d.id) != Some(EMPTY_ID)) } - } - }); + }) + .flat_map(|tt| match tt { + tt::TokenTree::Subtree(mut tt) => { + reverse_fixups(&mut tt, token_map, undo_info); + SmallVec::from_const([tt.into()]) + } + tt::TokenTree::Leaf(leaf) => { + if let Some(id) = token_map.synthetic_token_id(leaf.id()) { + let original = undo_info.original[id.0 as usize].clone(); + if original.delimiter.is_none() { + original.token_trees.into() + } else { + SmallVec::from_const([original.into()]) + } + } else { + SmallVec::from_const([leaf.into()]) + } + } + }) + .collect(); } #[cfg(test)] @@ -319,6 +329,31 @@ mod tests { use super::reverse_fixups; + // The following three functions are only meant to check partial structural equivalence of + // `TokenTree`s, see the last assertion in `check()`. + fn check_leaf_eq(a: &tt::Leaf, b: &tt::Leaf) -> bool { + match (a, b) { + (tt::Leaf::Literal(a), tt::Leaf::Literal(b)) => a.text == b.text, + (tt::Leaf::Punct(a), tt::Leaf::Punct(b)) => a.char == b.char, + (tt::Leaf::Ident(a), tt::Leaf::Ident(b)) => a.text == b.text, + _ => false, + } + } + + fn check_subtree_eq(a: &tt::Subtree, b: &tt::Subtree) -> bool { + a.delimiter.map(|it| it.kind) == b.delimiter.map(|it| it.kind) + && a.token_trees.len() == b.token_trees.len() + && a.token_trees.iter().zip(&b.token_trees).all(|(a, b)| check_tt_eq(a, b)) + } + + fn check_tt_eq(a: &tt::TokenTree, b: &tt::TokenTree) -> bool { + match (a, b) { + (tt::TokenTree::Leaf(a), tt::TokenTree::Leaf(b)) => check_leaf_eq(a, b), + (tt::TokenTree::Subtree(a), tt::TokenTree::Subtree(b)) => check_subtree_eq(a, b), + _ => false, + } + } + #[track_caller] fn check(ra_fixture: &str, mut expect: Expect) { let parsed = syntax::SourceFile::parse(ra_fixture); @@ -331,17 +366,15 @@ mod tests { fixups.append, ); - let mut actual = tt.to_string(); - actual.push('\n'); + let actual = format!("{}\n", tt); expect.indent(false); expect.assert_eq(&actual); // the fixed-up tree should be syntactically valid let (parse, _) = mbe::token_tree_to_syntax_node(&tt, ::mbe::TopEntryPoint::MacroItems); - assert_eq!( - parse.errors(), - &[], + assert!( + parse.errors().is_empty(), "parse has syntax errors. parse tree:\n{:#?}", parse.syntax_node() ); @@ -349,9 +382,12 @@ mod tests { reverse_fixups(&mut tt, &tmap, &fixups.undo_info); // the fixed-up + reversed version should be equivalent to the original input - // (but token IDs don't matter) + // modulo token IDs and `Punct`s' spacing. let (original_as_tt, _) = mbe::syntax_node_to_token_tree(&parsed.syntax_node()); - assert_eq!(tt.to_string(), original_as_tt.to_string()); + assert!( + check_subtree_eq(&tt, &original_as_tt), + "different token tree: {tt:?}, {original_as_tt:?}" + ); } #[test] @@ -468,7 +504,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup} +fn foo () {a . __ra_fixup} "#]], ) } @@ -482,7 +518,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup ;} +fn foo () {a . __ra_fixup ;} "#]], ) } @@ -497,7 +533,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .__ra_fixup ; bar () ;} +fn foo () {a . __ra_fixup ; bar () ;} "#]], ) } @@ -525,7 +561,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {let x = a .__ra_fixup ;} +fn foo () {let x = a . __ra_fixup ;} "#]], ) } @@ -541,7 +577,7 @@ fn foo() { } "#, expect![[r#" -fn foo () {a .b ; bar () ;} +fn foo () {a . b ; bar () ;} "#]], ) } diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index a5b499fe8d9d..7352b003a491 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -814,7 +814,7 @@ impl<'a> InFile<&'a SyntaxNode> { pub fn original_syntax_node(self, db: &dyn db::AstDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, - // as we don't have node inputs otherwise and therefor can't find an `N` node in the input + // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { return Some(self.map(Clone::clone)); } else if !self.file_id.is_attr_macro(db) { @@ -926,7 +926,7 @@ impl InFile { pub fn original_ast_node(self, db: &dyn db::AstDatabase) -> Option> { // This kind of upmapping can only be achieved in attribute expanded files, - // as we don't have node inputs otherwise and therefor can't find an `N` node in the input + // as we don't have node inputs otherwise and therefore can't find an `N` node in the input if !self.file_id.is_macro() { return Some(self); } else if !self.file_id.is_attr_macro(db) { diff --git a/crates/hir-ty/Cargo.toml b/crates/hir-ty/Cargo.toml index ed13275bab8f..a1d6835bfaed 100644 --- a/crates/hir-ty/Cargo.toml +++ b/crates/hir-ty/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/hir-ty/src/lib.rs b/crates/hir-ty/src/lib.rs index b68c764bdca0..39514fc44e6c 100644 --- a/crates/hir-ty/src/lib.rs +++ b/crates/hir-ty/src/lib.rs @@ -53,7 +53,7 @@ pub use builder::{ParamKind, TyBuilder}; pub use chalk_ext::*; pub use infer::{ could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic, - InferenceResult, + InferenceResult, OverloadedDeref, PointerCast, }; pub use interner::Interner; pub use lower::{ @@ -523,7 +523,7 @@ where } pub fn callable_sig_from_fnonce( - self_ty: &Canonical, + self_ty: &Ty, env: Arc, db: &dyn HirDatabase, ) -> Option { @@ -531,27 +531,28 @@ pub fn callable_sig_from_fnonce( let fn_once_trait = FnTrait::FnOnce.get_id(db, krate)?; let output_assoc_type = db.trait_data(fn_once_trait).associated_type_by_name(&name![Output])?; - let mut kinds = self_ty.binders.interned().to_vec(); let b = TyBuilder::trait_ref(db, fn_once_trait); if b.remaining() != 2 { return None; } - let fn_once = b - .push(self_ty.value.clone()) - .fill_with_bound_vars(DebruijnIndex::INNERMOST, kinds.len()) - .build(); - kinds.extend(fn_once.substitution.iter(Interner).skip(1).map(|x| { - let vk = match x.data(Interner) { - chalk_ir::GenericArgData::Ty(_) => { - chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) - } - chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, - chalk_ir::GenericArgData::Const(c) => { - chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) - } - }; - chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) - })); + let fn_once = b.push(self_ty.clone()).fill_with_bound_vars(DebruijnIndex::INNERMOST, 0).build(); + let kinds = fn_once + .substitution + .iter(Interner) + .skip(1) + .map(|x| { + let vk = match x.data(Interner) { + chalk_ir::GenericArgData::Ty(_) => { + chalk_ir::VariableKind::Ty(chalk_ir::TyVariableKind::General) + } + chalk_ir::GenericArgData::Lifetime(_) => chalk_ir::VariableKind::Lifetime, + chalk_ir::GenericArgData::Const(c) => { + chalk_ir::VariableKind::Const(c.data(Interner).ty.clone()) + } + }; + chalk_ir::WithKind::new(vk, UniverseIndex::ROOT) + }) + .collect::>(); // FIXME: chalk refuses to solve `>::Output == ^0.1`, so we first solve // `>` and then replace `^0.0` with the concrete argument tuple. @@ -563,21 +564,16 @@ pub fn callable_sig_from_fnonce( Some(Solution::Unique(vars)) => vars.value.subst, _ => return None, }; - let args = subst.at(Interner, self_ty.binders.interned().len()).ty(Interner)?; + let args = subst.at(Interner, 0).ty(Interner)?; let params = match args.kind(Interner) { chalk_ir::TyKind::Tuple(_, subst) => { subst.iter(Interner).filter_map(|arg| arg.ty(Interner).cloned()).collect::>() } _ => return None, }; - if params.iter().any(|ty| ty.is_unknown()) { - return None; - } - let fn_once = TyBuilder::trait_ref(db, fn_once_trait) - .push(self_ty.value.clone()) - .push(args.clone()) - .build(); + let fn_once = + TyBuilder::trait_ref(db, fn_once_trait).push(self_ty.clone()).push(args.clone()).build(); let projection = TyBuilder::assoc_type_projection(db, output_assoc_type, Some(fn_once.substitution.clone())) .build(); diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs index 50859475e1d9..8bcfa2728f07 100644 --- a/crates/hir-ty/src/method_resolution.rs +++ b/crates/hir-ty/src/method_resolution.rs @@ -541,7 +541,7 @@ pub struct ReceiverAdjustments { impl ReceiverAdjustments { pub(crate) fn apply(&self, table: &mut InferenceTable<'_>, ty: Ty) -> (Ty, Vec) { - let mut ty = ty; + let mut ty = table.resolve_ty_shallow(&ty); let mut adjust = Vec::new(); for _ in 0..self.autoderefs { match autoderef::autoderef_step(table, ty.clone()) { diff --git a/crates/hir-ty/src/tests/method_resolution.rs b/crates/hir-ty/src/tests/method_resolution.rs index ac8edb841a58..5d76d185ffc0 100644 --- a/crates/hir-ty/src/tests/method_resolution.rs +++ b/crates/hir-ty/src/tests/method_resolution.rs @@ -164,16 +164,16 @@ fn infer_associated_method_with_modules() { check_infer( r#" mod a { - struct A; + pub struct A; impl A { pub fn thing() -> A { A {} }} } mod b { - struct B; + pub struct B; impl B { pub fn thing() -> u32 { 99 }} - mod c { - struct C; + pub mod c { + pub struct C; impl C { pub fn thing() -> C { C {} }} } } @@ -186,22 +186,22 @@ fn infer_associated_method_with_modules() { } "#, expect![[r#" - 55..63 '{ A {} }': A - 57..61 'A {}': A - 125..131 '{ 99 }': u32 - 127..129 '99': u32 - 201..209 '{ C {} }': C - 203..207 'C {}': C - 240..324 '{ ...g(); }': () - 250..251 'x': A - 254..265 'a::A::thing': fn thing() -> A - 254..267 'a::A::thing()': A - 277..278 'y': u32 - 281..292 'b::B::thing': fn thing() -> u32 - 281..294 'b::B::thing()': u32 - 304..305 'z': C - 308..319 'c::C::thing': fn thing() -> C - 308..321 'c::C::thing()': C + 59..67 '{ A {} }': A + 61..65 'A {}': A + 133..139 '{ 99 }': u32 + 135..137 '99': u32 + 217..225 '{ C {} }': C + 219..223 'C {}': C + 256..340 '{ ...g(); }': () + 266..267 'x': A + 270..281 'a::A::thing': fn thing() -> A + 270..283 'a::A::thing()': A + 293..294 'y': u32 + 297..308 'b::B::thing': fn thing() -> u32 + 297..310 'b::B::thing()': u32 + 320..321 'z': C + 324..335 'c::C::thing': fn thing() -> C + 324..337 'c::C::thing()': C "#]], ); } diff --git a/crates/hir-ty/src/tests/regression.rs b/crates/hir-ty/src/tests/regression.rs index a155adcec6c3..4e46397459d5 100644 --- a/crates/hir-ty/src/tests/regression.rs +++ b/crates/hir-ty/src/tests/regression.rs @@ -1707,3 +1707,19 @@ impl Trait for [T; N] { "#, ); } + +#[test] +fn unsize_array_with_inference_variable() { + check_types( + r#" +//- minicore: try, slice +use core::ops::ControlFlow; +fn foo() -> ControlFlow<(), [usize; 1]> { loop {} } +fn bar() -> ControlFlow<(), ()> { + let a = foo()?.len(); + //^ usize + ControlFlow::Continue(()) +} +"#, + ); +} diff --git a/crates/hir-ty/src/tests/simple.rs b/crates/hir-ty/src/tests/simple.rs index 080e2ac1b8e1..d7431443b83d 100644 --- a/crates/hir-ty/src/tests/simple.rs +++ b/crates/hir-ty/src/tests/simple.rs @@ -214,7 +214,7 @@ fn infer_paths() { fn a() -> u32 { 1 } mod b { - fn c() -> u32 { 1 } + pub fn c() -> u32 { 1 } } fn test() { @@ -225,13 +225,13 @@ fn test() { expect![[r#" 14..19 '{ 1 }': u32 16..17 '1': u32 - 47..52 '{ 1 }': u32 - 49..50 '1': u32 - 66..90 '{ ...c(); }': () - 72..73 'a': fn a() -> u32 - 72..75 'a()': u32 - 81..85 'b::c': fn c() -> u32 - 81..87 'b::c()': u32 + 51..56 '{ 1 }': u32 + 53..54 '1': u32 + 70..94 '{ ...c(); }': () + 76..77 'a': fn a() -> u32 + 76..79 'a()': u32 + 85..89 'b::c': fn c() -> u32 + 85..91 'b::c()': u32 "#]], ); } @@ -1856,7 +1856,7 @@ fn not_shadowing_module_by_primitive() { check_types( r#" //- /str.rs -fn foo() -> u32 {0} +pub fn foo() -> u32 {0} //- /main.rs mod str; diff --git a/crates/hir-ty/src/tests/traits.rs b/crates/hir-ty/src/tests/traits.rs index 7d42b8b9bc8d..3d7194b6f446 100644 --- a/crates/hir-ty/src/tests/traits.rs +++ b/crates/hir-ty/src/tests/traits.rs @@ -1706,7 +1706,7 @@ fn where_clause_trait_in_scope_for_method_resolution() { check_types( r#" mod foo { - trait Trait { + pub trait Trait { fn foo(&self) -> u32 { 0 } } } @@ -1723,7 +1723,7 @@ fn super_trait_method_resolution() { check_infer( r#" mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1735,15 +1735,15 @@ fn test(x: T, y: U) { y.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 181..182 'x': T - 187..188 'y': U - 193..222 '{ ...o(); }': () - 199..200 'x': T - 199..206 'x.foo()': u32 - 212..213 'y': U - 212..219 'y.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 185..186 'x': T + 191..192 'y': U + 197..226 '{ ...o(); }': () + 203..204 'x': T + 203..210 'x.foo()': u32 + 216..217 'y': U + 216..223 'y.foo()': u32 "#]], ); } @@ -1754,7 +1754,7 @@ fn super_trait_impl_trait_method_resolution() { r#" //- minicore: sized mod foo { - trait SuperTrait { + pub trait SuperTrait { fn foo(&self) -> u32 {} } } @@ -1764,12 +1764,12 @@ fn test(x: &impl Trait1) { x.foo(); }"#, expect![[r#" - 49..53 'self': &Self - 62..64 '{}': u32 - 115..116 'x': &impl Trait1 - 132..148 '{ ...o(); }': () - 138..139 'x': &impl Trait1 - 138..145 'x.foo()': u32 + 53..57 'self': &Self + 66..68 '{}': u32 + 119..120 'x': &impl Trait1 + 136..152 '{ ...o(); }': () + 142..143 'x': &impl Trait1 + 142..149 'x.foo()': u32 "#]], ); } diff --git a/crates/hir/Cargo.toml b/crates/hir/Cargo.toml index e1418de3cdc2..f780e3f53c85 100644 --- a/crates/hir/Cargo.toml +++ b/crates/hir/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index cbd9bf32a548..cbbcaebb4285 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -117,7 +117,7 @@ pub use { name::{known, Name}, ExpandResult, HirFileId, InFile, MacroFile, Origin, }, - hir_ty::display::HirDisplay, + hir_ty::{display::HirDisplay, PointerCast, Safety}, }; // These are negative re-exports: pub using these names is forbidden, they @@ -2997,8 +2997,7 @@ impl Type { TyKind::Function(_) => Callee::FnPtr, TyKind::FnDef(..) => Callee::Def(self.ty.callable_def(db)?), _ => { - let ty = hir_ty::replace_errors_with_variables(&self.ty); - let sig = hir_ty::callable_sig_from_fnonce(&ty, self.env.clone(), db)?; + let sig = hir_ty::callable_sig_from_fnonce(&self.ty, self.env.clone(), db)?; return Some(Callable { ty: self.clone(), sig, @@ -3651,6 +3650,28 @@ impl From for ScopeDef { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Adjust { + /// Go from ! to any type. + NeverToAny, + /// Dereference once, producing a place. + Deref(Option), + /// Take the address and produce either a `&` or `*` pointer. + Borrow(AutoBorrow), + Pointer(PointerCast), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum AutoBorrow { + /// Converts from T to &T. + Ref(Mutability), + /// Converts from T to *T. + RawPtr(Mutability), +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct OverloadedDeref(pub Mutability); + pub trait HasVisibility { fn visibility(&self, db: &dyn HirDatabase) -> Visibility; fn is_visible_from(&self, db: &dyn HirDatabase, module: Module) -> bool { diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index 119ec3210e17..2e1f88ba0904 100644 --- a/crates/hir/src/semantics.rs +++ b/crates/hir/src/semantics.rs @@ -29,9 +29,10 @@ use crate::{ db::HirDatabase, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, source_analyzer::{resolve_hir_path, SourceAnalyzer}, - Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, DeriveHelper, Field, Function, - HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, - Name, Path, ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef, + Access, Adjust, AutoBorrow, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, + DeriveHelper, Field, Function, HasSource, HirFileId, Impl, InFile, Label, LifetimeParam, Local, + Macro, Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, ToolModule, Trait, Type, + TypeAlias, TypeParam, VariantDef, }; #[derive(Debug, Clone, PartialEq, Eq)] @@ -333,9 +334,8 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> { self.imp.resolve_trait(trait_) } - // FIXME: Figure out a nice interface to inspect adjustments - pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { - self.imp.is_implicit_reborrow(expr) + pub fn expr_adjustments(&self, expr: &ast::Expr) -> Option> { + self.imp.expr_adjustments(expr) } pub fn type_of_expr(&self, expr: &ast::Expr) -> Option { @@ -1067,8 +1067,29 @@ impl<'db> SemanticsImpl<'db> { } } - fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option { - self.analyze(expr.syntax())?.is_implicit_reborrow(self.db, expr) + fn expr_adjustments(&self, expr: &ast::Expr) -> Option> { + let mutability = |m| match m { + hir_ty::Mutability::Not => Mutability::Shared, + hir_ty::Mutability::Mut => Mutability::Mut, + }; + self.analyze(expr.syntax())?.expr_adjustments(self.db, expr).map(|it| { + it.iter() + .map(|adjust| match adjust.kind { + hir_ty::Adjust::NeverToAny => Adjust::NeverToAny, + hir_ty::Adjust::Deref(Some(hir_ty::OverloadedDeref(m))) => { + Adjust::Deref(Some(OverloadedDeref(mutability(m)))) + } + hir_ty::Adjust::Deref(None) => Adjust::Deref(None), + hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::RawPtr(m)) => { + Adjust::Borrow(AutoBorrow::RawPtr(mutability(m))) + } + hir_ty::Adjust::Borrow(hir_ty::AutoBorrow::Ref(m)) => { + Adjust::Borrow(AutoBorrow::Ref(mutability(m))) + } + hir_ty::Adjust::Pointer(pc) => Adjust::Pointer(pc), + }) + .collect() + }) } fn type_of_expr(&self, expr: &ast::Expr) -> Option { diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index f86c57100536..91ea1c24d14f 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -38,8 +38,7 @@ use hir_ty::{ UnsafeExpr, }, method_resolution::{self, lang_names_for_bin_op}, - Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, - TyLoweringContext, + Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, TyLoweringContext, }; use itertools::Itertools; use smallvec::SmallVec; @@ -156,21 +155,14 @@ impl SourceAnalyzer { Some(res) } - pub(crate) fn is_implicit_reborrow( + pub(crate) fn expr_adjustments( &self, db: &dyn HirDatabase, expr: &ast::Expr, - ) -> Option { + ) -> Option<&[Adjustment]> { let expr_id = self.expr_id(db, expr)?; let infer = self.infer.as_ref()?; - let adjustments = infer.expr_adjustments.get(&expr_id)?; - adjustments.windows(2).find_map(|slice| match slice { - &[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m { - hir_ty::Mutability::Mut => Mutability::Mut, - hir_ty::Mutability::Not => Mutability::Shared, - }), - _ => None, - }) + infer.expr_adjustments.get(&expr_id).map(|v| &**v) } pub(crate) fn type_of_expr( diff --git a/crates/ide-assists/Cargo.toml b/crates/ide-assists/Cargo.toml index 57a41f3d9a93..e781c0a016d5 100644 --- a/crates/ide-assists/Cargo.toml +++ b/crates/ide-assists/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs index 62cf5ab4f37a..2b3793659cf7 100644 --- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs +++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs @@ -196,6 +196,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn foo(&self); fn bar(&self); @@ -213,6 +214,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn foo(&self); fn bar(&self); @@ -226,7 +228,7 @@ impl Foo for S { $0type Output; - const CONST: usize = 42; + const CONST_2: i32; fn foo(&self) { todo!() @@ -379,14 +381,14 @@ impl Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -439,14 +441,14 @@ impl bar::Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -464,14 +466,14 @@ impl foo::Foo for S { r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub struct Bar; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -489,7 +491,7 @@ impl foo::Foo for S { add_missing_impl_members, r#" mod foo { - trait Foo { fn foo(&self, bar: T); } + pub trait Foo { fn foo(&self, bar: T); } pub struct Param; } struct Param; @@ -497,7 +499,7 @@ struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { - trait Foo { fn foo(&self, bar: T); } + pub trait Foo { fn foo(&self, bar: T); } pub struct Param; } struct Param; @@ -518,7 +520,7 @@ impl foo::Foo for S { mod foo { pub struct Bar; impl Bar { type Assoc = u32; } - trait Foo { fn foo(&self, bar: Bar::Assoc); } + pub trait Foo { fn foo(&self, bar: Bar::Assoc); } } struct S; impl foo::Foo for S { $0 }"#, @@ -526,7 +528,7 @@ impl foo::Foo for S { $0 }"#, mod foo { pub struct Bar; impl Bar { type Assoc = u32; } - trait Foo { fn foo(&self, bar: Bar::Assoc); } + pub trait Foo { fn foo(&self, bar: Bar::Assoc); } } struct S; impl foo::Foo for S { @@ -545,7 +547,7 @@ impl foo::Foo for S { mod foo { pub struct Bar; pub struct Baz; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { $0 }"#, @@ -553,7 +555,7 @@ impl foo::Foo for S { $0 }"#, mod foo { pub struct Bar; pub struct Baz; - trait Foo { fn foo(&self, bar: Bar); } + pub trait Foo { fn foo(&self, bar: Bar); } } struct S; impl foo::Foo for S { @@ -571,14 +573,14 @@ impl foo::Foo for S { r#" mod foo { pub trait Fn { type Output; } - trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } + pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } } struct S; impl foo::Foo for S { $0 }"#, r#" mod foo { pub trait Fn { type Output; } - trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } + pub trait Foo { fn foo(&self, bar: dyn Fn(u32) -> i32); } } struct S; impl foo::Foo for S { @@ -658,6 +660,7 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn valid(some: u32) -> bool { false } fn foo(some: u32) -> bool; @@ -669,13 +672,16 @@ trait Foo { type Output; const CONST: usize = 42; + const CONST_2: i32; fn valid(some: u32) -> bool { false } fn foo(some: u32) -> bool; } struct S; impl Foo for S { - $0fn valid(some: u32) -> bool { false } + $0const CONST: usize = 42; + + fn valid(some: u32) -> bool { false } }"#, ) } diff --git a/crates/ide-assists/src/handlers/extract_function.rs b/crates/ide-assists/src/handlers/extract_function.rs index 060588358492..c1e2f19ab18b 100644 --- a/crates/ide-assists/src/handlers/extract_function.rs +++ b/crates/ide-assists/src/handlers/extract_function.rs @@ -109,8 +109,6 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op let params = body.extracted_function_params(ctx, &container_info, locals_used.iter().copied()); - let extracted_from_trait_impl = body.extracted_from_trait_impl(); - let name = make_function_name(&semantics_scope); let fun = Function { @@ -129,8 +127,11 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op builder.replace(target_range, make_call(ctx, &fun, old_indent)); + let has_impl_wrapper = + insert_after.ancestors().any(|a| a.kind() == SyntaxKind::IMPL && a != insert_after); + let fn_def = match fun.self_param_adt(ctx) { - Some(adt) if extracted_from_trait_impl => { + Some(adt) if anchor == Anchor::Method && !has_impl_wrapper => { let fn_def = format_function(ctx, module, &fun, old_indent, new_indent + 1); generate_impl_text(&adt, &fn_def).replace("{\n\n", "{") } @@ -272,7 +273,7 @@ enum FunType { } /// Where to put extracted function definition -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq, Clone, Copy)] enum Anchor { /// Extract free function and put right after current top-level function Freestanding, @@ -1245,6 +1246,14 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option break, + SyntaxKind::IMPL => { + if body.extracted_from_trait_impl() && matches!(anchor, Anchor::Method) { + let impl_node = find_non_trait_impl(&next_ancestor); + if let target_node @ Some(_) = impl_node.as_ref().and_then(last_impl_member) { + return target_node; + } + } + } SyntaxKind::ITEM_LIST if !matches!(anchor, Anchor::Freestanding) => continue, SyntaxKind::ITEM_LIST => { if ancestors.peek().map(SyntaxNode::kind) == Some(SyntaxKind::MODULE) { @@ -1265,6 +1274,29 @@ fn node_to_insert_after(body: &FunctionBody, anchor: Anchor) -> Option Option { + let as_impl = ast::Impl::cast(trait_impl.clone())?; + let impl_type = Some(impl_type_name(&as_impl)?); + + let sibblings = trait_impl.parent()?.children(); + sibblings + .filter_map(ast::Impl::cast) + .find(|s| impl_type_name(s) == impl_type && !is_trait_impl(s)) +} + +fn last_impl_member(impl_node: &ast::Impl) -> Option { + let last_child = impl_node.assoc_item_list()?.assoc_items().last()?; + Some(last_child.syntax().clone()) +} + +fn is_trait_impl(node: &ast::Impl) -> bool { + node.trait_().is_some() +} + +fn impl_type_name(impl_node: &ast::Impl) -> Option { + Some(impl_node.self_ty()?.to_string()) +} + fn make_call(ctx: &AssistContext<'_>, fun: &Function, indent: IndentLevel) -> String { let ret_ty = fun.return_type(ctx); @@ -5051,6 +5083,236 @@ impl Struct { ); } + #[test] + fn extract_method_from_trait_with_existing_non_empty_impl_block() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + + #[test] + fn extract_function_from_trait_with_existing_non_empty_impl_block() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + let three_squared = $03 * 3$0; + self.0 + three_squared + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl Struct { + fn foo() {} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + let three_squared = fun_name(); + self.0 + three_squared + } +} + +fn $0fun_name() -> i32 { + 3 * 3 +} +"#, + ) + } + + #[test] + fn extract_method_from_trait_with_multiple_existing_impl_blocks() { + check_assist( + extract_function, + r#" +struct Struct(i32); +struct StructBefore(i32); +struct StructAfter(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl StructBefore { + fn foo(){} +} + +impl Struct { + fn foo(){} +} + +impl StructAfter { + fn foo(){} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +struct StructBefore(i32); +struct StructAfter(i32); +trait Trait { + fn bar(&self) -> i32; +} + +impl StructBefore { + fn foo(){} +} + +impl Struct { + fn foo(){} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl StructAfter { + fn foo(){} +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + + #[test] + fn extract_method_from_trait_with_multiple_existing_trait_impl_blocks() { + check_assist( + extract_function, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} +trait TraitBefore { + fn before(&self) -> i32; +} +trait TraitAfter { + fn after(&self) -> i32; +} + +impl TraitBefore for Struct { + fn before(&self) -> i32 { + 42 + } +} + +impl Struct { + fn foo(){} +} + +impl TraitAfter for Struct { + fn after(&self) -> i32 { + 42 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + $0self.0 + 2$0 + } +} +"#, + r#" +struct Struct(i32); +trait Trait { + fn bar(&self) -> i32; +} +trait TraitBefore { + fn before(&self) -> i32; +} +trait TraitAfter { + fn after(&self) -> i32; +} + +impl TraitBefore for Struct { + fn before(&self) -> i32 { + 42 + } +} + +impl Struct { + fn foo(){} + + fn $0fun_name(&self) -> i32 { + self.0 + 2 + } +} + +impl TraitAfter for Struct { + fn after(&self) -> i32 { + 42 + } +} + +impl Trait for Struct { + fn bar(&self) -> i32 { + self.fun_name() + } +} +"#, + ) + } + #[test] fn closure_arguments() { check_assist( diff --git a/crates/ide-assists/src/handlers/fix_visibility.rs b/crates/ide-assists/src/handlers/fix_visibility.rs index 876454302870..d9e00435ecf5 100644 --- a/crates/ide-assists/src/handlers/fix_visibility.rs +++ b/crates/ide-assists/src/handlers/fix_visibility.rs @@ -1,4 +1,4 @@ -use hir::{db::HirDatabase, HasSource, HasVisibility, PathResolution}; +use hir::{db::HirDatabase, HasSource, HasVisibility, ModuleDef, PathResolution, ScopeDef}; use ide_db::base_db::FileId; use syntax::{ ast::{self, HasVisibility as _}, @@ -18,7 +18,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; // fn frobnicate() {} // } // fn main() { -// m::frobnicate$0() {} +// m::frobnicate$0(); // } // ``` // -> @@ -27,7 +27,7 @@ use crate::{utils::vis_offset, AssistContext, AssistId, AssistKind, Assists}; // $0pub(crate) fn frobnicate() {} // } // fn main() { -// m::frobnicate() {} +// m::frobnicate(); // } // ``` pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { @@ -37,11 +37,15 @@ pub(crate) fn fix_visibility(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti fn add_vis_to_referenced_module_def(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { let path: ast::Path = ctx.find_node_at_offset()?; - let path_res = ctx.sema.resolve_path(&path)?; - let def = match path_res { - PathResolution::Def(def) => def, - _ => return None, - }; + let qualifier = path.qualifier()?; + let name_ref = path.segment()?.name_ref()?; + let qualifier_res = ctx.sema.resolve_path(&qualifier)?; + let PathResolution::Def(ModuleDef::Module(module)) = qualifier_res else { return None; }; + let (_, def) = module + .scope(ctx.db(), None) + .into_iter() + .find(|(name, _)| name.to_smol_str() == name_ref.text().as_str())?; + let ScopeDef::ModuleDef(def) = def else { return None; }; let current_module = ctx.sema.scope(path.syntax())?.module(); let target_module = def.module(ctx.db())?; diff --git a/crates/ide-assists/src/handlers/generate_enum_variant.rs b/crates/ide-assists/src/handlers/generate_enum_variant.rs index 35cd42908af2..0bcb5728311b 100644 --- a/crates/ide-assists/src/handlers/generate_enum_variant.rs +++ b/crates/ide-assists/src/handlers/generate_enum_variant.rs @@ -261,12 +261,12 @@ fn main() { } //- /foo.rs -enum Foo { +pub enum Foo { Bar, } ", r" -enum Foo { +pub enum Foo { Bar, Baz, } @@ -310,7 +310,7 @@ fn main() { generate_enum_variant, r" mod m { - enum Foo { + pub enum Foo { Bar, } } @@ -320,7 +320,7 @@ fn main() { ", r" mod m { - enum Foo { + pub enum Foo { Bar, Baz, } @@ -516,10 +516,10 @@ mod foo; use foo::Foo::Bar$0; //- /foo.rs -enum Foo {} +pub enum Foo {} ", r" -enum Foo { +pub enum Foo { Bar, } ", diff --git a/crates/ide-assists/src/handlers/generate_function.rs b/crates/ide-assists/src/handlers/generate_function.rs index c229127e48ff..57f198748cb7 100644 --- a/crates/ide-assists/src/handlers/generate_function.rs +++ b/crates/ide-assists/src/handlers/generate_function.rs @@ -1324,7 +1324,7 @@ fn foo() { generate_function, r" mod bar { - mod baz {} + pub mod baz {} } fn foo() { @@ -1333,7 +1333,7 @@ fn foo() { ", r" mod bar { - mod baz { + pub mod baz { pub(crate) fn my_fn() { ${0:todo!()} } diff --git a/crates/ide-assists/src/handlers/move_format_string_arg.rs b/crates/ide-assists/src/handlers/move_format_string_arg.rs index aa710d2ce651..11db6ae7f7b8 100644 --- a/crates/ide-assists/src/handlers/move_format_string_arg.rs +++ b/crates/ide-assists/src/handlers/move_format_string_arg.rs @@ -92,7 +92,7 @@ pub(crate) fn move_format_string_arg(acc: &mut Assists, ctx: &AssistContext<'_>) NodeOrToken::Node(n) => { format_to!(current_arg, "{n}"); }, - NodeOrToken::Token(t) if t.kind() == COMMA=> { + NodeOrToken::Token(t) if t.kind() == COMMA => { existing_args.push(current_arg.trim().into()); current_arg.clear(); }, @@ -238,14 +238,14 @@ fn main() { &add_macro_decl( r#" fn main() { - print!("{} {x + 1:b} {Struct(1, 2)}$0", 1); + print!("{:b} {x + 1:b} {Struct(1, 2)}$0", 1); } "#, ), &add_macro_decl( r#" fn main() { - print!("{} {:b} {}"$0, 1, x + 1, Struct(1, 2)); + print!("{:b} {:b} {}"$0, 1, x + 1, Struct(1, 2)); } "#, ), diff --git a/crates/ide-assists/src/handlers/remove_dbg.rs b/crates/ide-assists/src/handlers/remove_dbg.rs index 3d9cbff177ba..99ae60e07bcf 100644 --- a/crates/ide-assists/src/handlers/remove_dbg.rs +++ b/crates/ide-assists/src/handlers/remove_dbg.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use syntax::{ ast::{self, AstNode, AstToken}, - match_ast, NodeOrToken, SyntaxElement, TextSize, T, + match_ast, NodeOrToken, SyntaxElement, TextRange, TextSize, T, }; use crate::{AssistContext, AssistId, AssistKind, Assists}; @@ -22,7 +22,36 @@ use crate::{AssistContext, AssistId, AssistKind, Assists}; // } // ``` pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - let macro_call = ctx.find_node_at_offset::()?; + let macro_calls = if ctx.has_empty_selection() { + vec![ctx.find_node_at_offset::()?] + } else { + ctx.covering_element() + .as_node()? + .descendants() + .filter(|node| ctx.selection_trimmed().contains_range(node.text_range())) + .filter_map(ast::MacroCall::cast) + .collect() + }; + + let replacements = + macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::>(); + if replacements.is_empty() { + return None; + } + + acc.add( + AssistId("remove_dbg", AssistKind::Refactor), + "Remove dbg!()", + ctx.selection_trimmed(), + |builder| { + for (range, text) in replacements { + builder.replace(range, text); + } + }, + ) +} + +fn compute_dbg_replacement(macro_call: ast::MacroCall) -> Option<(TextRange, String)> { let tt = macro_call.token_tree()?; let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?); if macro_call.path()?.segment()?.name_ref()?.text() != "dbg" @@ -41,7 +70,7 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( let macro_expr = ast::MacroExpr::cast(macro_call.syntax().parent()?)?; let parent = macro_expr.syntax().parent()?; - let (range, text) = match &*input_expressions { + Some(match &*input_expressions { // dbg!() [] => { match_ast! { @@ -107,10 +136,6 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<( } // dbg!(expr0, expr1, ...) exprs => (macro_call.syntax().text_range(), format!("({})", exprs.iter().format(", "))), - }; - - acc.add(AssistId("remove_dbg", AssistKind::Refactor), "Remove dbg!()", range, |builder| { - builder.replace(range, text); }) } @@ -238,4 +263,28 @@ fn foo() { check(r#"$0dbg!(0, 1)"#, r#"(0, 1)"#); check(r#"$0dbg!(0, (1, 2))"#, r#"(0, (1, 2))"#); } + + #[test] + fn test_range() { + check( + r#" +fn f() { + dbg!(0) + $0dbg!(1); + dbg!(())$0 +} +"#, + r#" +fn f() { + dbg!(0) + 1; + () +} +"#, + ); + } + + #[test] + fn test_range_partial() { + check_assist_not_applicable(remove_dbg, r#"$0dbg$0!(0)"#); + check_assist_not_applicable(remove_dbg, r#"$0dbg!(0$0)"#); + } } diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs index f9ba289ee175..6fa15b28e4ef 100644 --- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs +++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs @@ -1019,8 +1019,6 @@ struct Foo { impl foo::Bar for Foo { $0type Qux; - const Baz: usize = 42; - const Fez: usize; fn foo() { diff --git a/crates/ide-assists/src/tests/generated.rs b/crates/ide-assists/src/tests/generated.rs index 029d169899bb..c09317572acf 100644 --- a/crates/ide-assists/src/tests/generated.rs +++ b/crates/ide-assists/src/tests/generated.rs @@ -741,7 +741,7 @@ mod m { fn frobnicate() {} } fn main() { - m::frobnicate$0() {} + m::frobnicate$0(); } "#####, r#####" @@ -749,7 +749,7 @@ mod m { $0pub(crate) fn frobnicate() {} } fn main() { - m::frobnicate() {} + m::frobnicate(); } "#####, ) diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 307e67927056..68c31b4f8e92 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -119,6 +119,10 @@ pub fn filter_assoc_items( (default_methods, def.body()), (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) ), + ast::AssocItem::Const(def) => matches!( + (default_methods, def.body()), + (DefaultMethods::Only, Some(_)) | (DefaultMethods::No, None) + ), _ => default_methods == DefaultMethods::No, }) .collect::>() diff --git a/crates/ide-completion/Cargo.toml b/crates/ide-completion/Cargo.toml index 75835bce95da..11310e2f1291 100644 --- a/crates/ide-completion/Cargo.toml +++ b/crates/ide-completion/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false 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 e82cbfdcb840..7384a3f2d80b 100644 --- a/crates/ide-completion/src/completions/item_list/trait_impl.rs +++ b/crates/ide-completion/src/completions/item_list/trait_impl.rs @@ -157,7 +157,7 @@ fn complete_trait_impl( add_function_impl(acc, ctx, replacement_range, func, hir_impl) } (hir::AssocItem::TypeAlias(type_alias), All | TypeAlias) => { - add_type_alias_impl(acc, ctx, replacement_range, type_alias) + add_type_alias_impl(acc, ctx, replacement_range, type_alias, hir_impl) } (hir::AssocItem::Const(const_), All | Const) => { add_const_impl(acc, ctx, replacement_range, const_, hir_impl) @@ -236,9 +236,7 @@ fn get_transformed_assoc_item( ); transform.apply(assoc_item.syntax()); - if let ast::AssocItem::Fn(func) = &assoc_item { - func.remove_attrs_and_docs(); - } + assoc_item.remove_attrs_and_docs(); Some(assoc_item) } @@ -247,24 +245,50 @@ fn add_type_alias_impl( ctx: &CompletionContext<'_>, replacement_range: TextRange, type_alias: hir::TypeAlias, + impl_def: hir::Impl, ) { - let alias_name = type_alias.name(ctx.db); - let (alias_name, escaped_name) = - (alias_name.unescaped().to_smol_str(), alias_name.to_smol_str()); + let alias_name = type_alias.name(ctx.db).unescaped().to_smol_str(); let label = format!("type {} =", alias_name); - let replacement = format!("type {} = ", escaped_name); let mut item = CompletionItem::new(SymbolKind::TypeAlias, replacement_range, label); item.lookup_by(format!("type {}", alias_name)) .set_documentation(type_alias.docs(ctx.db)) .set_relevance(CompletionRelevance { is_item_from_trait: true, ..Default::default() }); - match ctx.config.snippet_cap { - Some(cap) => item - .snippet_edit(cap, TextEdit::replace(replacement_range, format!("{}$0;", replacement))), - None => item.text_edit(TextEdit::replace(replacement_range, replacement)), - }; - item.add_to(acc); + + if let Some(source) = ctx.sema.source(type_alias) { + let assoc_item = ast::AssocItem::TypeAlias(source.value); + if let Some(transformed_item) = get_transformed_assoc_item(ctx, assoc_item, impl_def) { + let transformed_ty = match transformed_item { + ast::AssocItem::TypeAlias(ty) => ty, + _ => unreachable!(), + }; + + let start = transformed_ty.syntax().text_range().start(); + let Some(end) = transformed_ty + .eq_token() + .map(|tok| tok.text_range().start()) + .or(transformed_ty.semicolon_token().map(|tok| tok.text_range().start())) else { return }; + + let len = end - start; + let mut decl = transformed_ty.syntax().text().slice(..len).to_string(); + if !decl.ends_with(' ') { + decl.push(' '); + } + decl.push_str("= "); + + match ctx.config.snippet_cap { + Some(cap) => { + let snippet = format!("{}$0;", decl); + item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet)); + } + None => { + item.text_edit(TextEdit::replace(replacement_range, decl)); + } + }; + item.add_to(acc); + } + } } fn add_const_impl( @@ -309,7 +333,6 @@ fn add_const_impl( } fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> String { - const_.remove_attrs_and_docs(); let const_ = if needs_whitespace { insert_whitespace_into_node::insert_ws_into(const_.syntax().clone()) } else { @@ -333,8 +356,6 @@ fn make_const_compl_syntax(const_: &ast::Const, needs_whitespace: bool) -> Strin } fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { - node.remove_attrs_and_docs(); - let node = if needs_whitespace { insert_whitespace_into_node::insert_ws_into(node.syntax().clone()) } else { @@ -350,9 +371,7 @@ fn function_declaration(node: &ast::Fn, needs_whitespace: bool) -> String { .map_or(end, |f| f.text_range().start()); let len = end - start; - let range = TextRange::new(0.into(), len); - - let syntax = node.text().slice(range).to_string(); + let syntax = node.text().slice(..len).to_string(); syntax.trim_end().to_owned() } @@ -1160,6 +1179,106 @@ impl Foo for Test { $0 } } +"#, + ); + } + + #[test] + fn includes_gat_generics() { + check_edit( + "type Ty", + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + $0 +} +"#, + r#" +trait Tr<'b> { + type Ty<'a: 'b, T: Copy, const C: usize>; +} + +impl<'b> Tr<'b> for () { + type Ty<'a: 'b, T: Copy, const C: usize> = $0; +} +"#, + ); + } + + #[test] + fn strips_comments() { + check_edit( + "fn func", + r#" +trait Tr { + /// docs + #[attr] + fn func(); +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + fn func(); +} +impl Tr for () { + fn func() { + $0 +} +} +"#, + ); + check_edit( + "const C", + r#" +trait Tr { + /// docs + #[attr] + const C: usize; +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + const C: usize; +} +impl Tr for () { + const C: usize = $0; +} +"#, + ); + check_edit( + "type Item", + r#" +trait Tr { + /// docs + #[attr] + type Item; +} +impl Tr for () { + $0 +} +"#, + r#" +trait Tr { + /// docs + #[attr] + type Item; +} +impl Tr for () { + type Item = $0; +} "#, ); } diff --git a/crates/ide-completion/src/context/analysis.rs b/crates/ide-completion/src/context/analysis.rs index 04111ec7efaa..c142a7305f9e 100644 --- a/crates/ide-completion/src/context/analysis.rs +++ b/crates/ide-completion/src/context/analysis.rs @@ -681,9 +681,13 @@ fn classify_name_ref( ast::Item::ExternBlock(it) => it.extern_item_list().is_none(), ast::Item::Fn(it) => it.body().is_none(), ast::Item::Impl(it) => it.assoc_item_list().is_none(), - ast::Item::Module(it) => it.item_list().is_none(), + ast::Item::Module(it) => { + it.item_list().is_none() && it.semicolon_token().is_none() + } ast::Item::Static(it) => it.body().is_none(), - ast::Item::Struct(it) => it.field_list().is_none(), + ast::Item::Struct(it) => { + it.field_list().is_none() && it.semicolon_token().is_none() + } ast::Item::Trait(it) => it.assoc_item_list().is_none(), ast::Item::TypeAlias(it) => it.ty().is_none(), ast::Item::Union(it) => it.record_field_list().is_none(), diff --git a/crates/ide-completion/src/tests/item_list.rs b/crates/ide-completion/src/tests/item_list.rs index 5076c6e86cae..8ed6cb3cf867 100644 --- a/crates/ide-completion/src/tests/item_list.rs +++ b/crates/ide-completion/src/tests/item_list.rs @@ -245,3 +245,35 @@ impl Test for () { "#]], ); } + +#[test] +fn after_unit_struct() { + check( + r#"struct S; f$0"#, + expect![[r#" + ma makro!(…) macro_rules! makro + md module + kw const + kw crate:: + kw enum + kw extern + kw fn + kw impl + kw mod + kw pub + kw pub(crate) + kw pub(super) + kw self:: + kw static + kw struct + kw trait + kw type + kw union + kw unsafe + kw use + sn macro_rules + sn tfn (Test function) + sn tmod (Test module) + "#]], + ); +} diff --git a/crates/ide-db/Cargo.toml b/crates/ide-db/Cargo.toml index cf0bcd5c96b2..f48cce58c6e7 100644 --- a/crates/ide-db/Cargo.toml +++ b/crates/ide-db/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/ide-db/src/line_index.rs b/crates/ide-db/src/line_index.rs index 75d49ff2fd77..1b8f56187a02 100644 --- a/crates/ide-db/src/line_index.rs +++ b/crates/ide-db/src/line_index.rs @@ -58,8 +58,11 @@ impl LineIndex { let mut utf16_lines = NoHashHashMap::default(); let mut utf16_chars = Vec::new(); - let mut newlines = vec![0.into()]; - let mut curr_row @ mut curr_col = 0.into(); + let mut newlines = Vec::with_capacity(16); + newlines.push(TextSize::from(0)); + + let mut curr_row = 0.into(); + let mut curr_col = 0.into(); let mut line = 0; for c in text.chars() { let c_len = TextSize::of(c); diff --git a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs index ac6c6e8feeea..313346ee1315 100644 --- a/crates/ide-db/src/syntax_helpers/format_string_exprs.rs +++ b/crates/ide-db/src/syntax_helpers/format_string_exprs.rs @@ -104,6 +104,11 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec), ()> { extracted_expressions.push(Arg::Placeholder); state = State::NotArg; } + (State::MaybeArg, ':') => { + output.push(chr); + extracted_expressions.push(Arg::Placeholder); + state = State::FormatOpts; + } (State::MaybeArg, _) => { if matches!(chr, '\\' | '$') { current_expr.push('\\'); @@ -118,44 +123,41 @@ pub fn parse_format_exprs(input: &str) -> Result<(String, Vec), ()> { state = State::Expr; } } - (State::Ident | State::Expr, '}') => { - if inexpr_open_count == 0 { - output.push(chr); - - if matches!(state, State::Expr) { - extracted_expressions.push(Arg::Expr(current_expr.trim().into())); - } else { - extracted_expressions.push(Arg::Ident(current_expr.trim().into())); - } - - current_expr = String::new(); - state = State::NotArg; - } else { - // We're closing one brace met before inside of the expression. - current_expr.push(chr); - inexpr_open_count -= 1; - } - } (State::Ident | State::Expr, ':') if matches!(chars.peek(), Some(':')) => { // path separator state = State::Expr; current_expr.push_str("::"); chars.next(); } - (State::Ident | State::Expr, ':') => { + (State::Ident | State::Expr, ':' | '}') => { if inexpr_open_count == 0 { - // We're outside of braces, thus assume that it's a specifier, like "{Some(value):?}" - output.push(chr); + let trimmed = current_expr.trim(); - if matches!(state, State::Expr) { - extracted_expressions.push(Arg::Expr(current_expr.trim().into())); + // if the expression consists of a single number, like "0" or "12", it can refer to + // format args in the order they are specified. + // see: https://doc.rust-lang.org/std/fmt/#positional-parameters + if trimmed.chars().fold(true, |only_num, c| c.is_ascii_digit() && only_num) { + output.push_str(trimmed); + } else if matches!(state, State::Expr) { + extracted_expressions.push(Arg::Expr(trimmed.into())); } else { - extracted_expressions.push(Arg::Ident(current_expr.trim().into())); + extracted_expressions.push(Arg::Ident(trimmed.into())); } - current_expr = String::new(); - state = State::FormatOpts; - } else { + output.push(chr); + current_expr.clear(); + state = if chr == ':' { + State::FormatOpts + } else if chr == '}' { + State::NotArg + } else { + unreachable!() + }; + } else if chr == '}' { + // We're closing one brace met before inside of the expression. + current_expr.push(chr); + inexpr_open_count -= 1; + } else if chr == ':' { // We're inside of braced expression, assume that it's a struct field name/value delimiter. current_expr.push(chr); } @@ -219,6 +221,10 @@ mod tests { ("{expr} is {2 + 2}", expect![["{} is {}; expr, 2 + 2"]]), ("{expr:?}", expect![["{:?}; expr"]]), ("{expr:1$}", expect![[r"{:1\$}; expr"]]), + ("{:1$}", expect![[r"{:1\$}; $1"]]), + ("{:>padding$}", expect![[r"{:>padding\$}; $1"]]), + ("{}, {}, {0}", expect![[r"{}, {}, {0}; $1, $2"]]), + ("{}, {}, {0:b}", expect![[r"{}, {}, {0:b}; $1, $2"]]), ("{$0}", expect![[r"{}; \$0"]]), ("{malformed", expect![["-"]]), ("malformed}", expect![["-"]]), diff --git a/crates/ide-diagnostics/Cargo.toml b/crates/ide-diagnostics/Cargo.toml index e1d146f4ee56..7e9a1125d751 100644 --- a/crates/ide-diagnostics/Cargo.toml +++ b/crates/ide-diagnostics/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/ide-diagnostics/src/handlers/macro_error.rs b/crates/ide-diagnostics/src/handlers/macro_error.rs index 43ff4ed5a6c8..870c78d1f1eb 100644 --- a/crates/ide-diagnostics/src/handlers/macro_error.rs +++ b/crates/ide-diagnostics/src/handlers/macro_error.rs @@ -5,10 +5,7 @@ use crate::{Diagnostic, DiagnosticsContext}; // This diagnostic is shown for macro expansion errors. pub(crate) fn macro_error(ctx: &DiagnosticsContext<'_>, d: &hir::MacroError) -> Diagnostic { // Use more accurate position if available. - let display_range = d - .precise_location - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone()).range); - + let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); Diagnostic::new("macro-error", d.message.clone(), display_range).experimental() } diff --git a/crates/ide-diagnostics/src/handlers/no_such_field.rs b/crates/ide-diagnostics/src/handlers/no_such_field.rs index a80299106bd3..d8f2a9de9818 100644 --- a/crates/ide-diagnostics/src/handlers/no_such_field.rs +++ b/crates/ide-diagnostics/src/handlers/no_such_field.rs @@ -268,12 +268,12 @@ fn main() { foo::Foo { bar: 3, $0baz: false}; } //- /foo.rs -struct Foo { +pub struct Foo { bar: i32 } "#, r#" -struct Foo { +pub struct Foo { bar: i32, pub(crate) baz: bool } diff --git a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs index 4b43124757f0..87531f4acfb7 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_macro_call.rs @@ -9,10 +9,7 @@ pub(crate) fn unresolved_macro_call( d: &hir::UnresolvedMacroCall, ) -> Diagnostic { // Use more accurate position if available. - let display_range = d - .precise_location - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.macro_call.clone()).range); - + let display_range = ctx.resolve_precise_location(&d.macro_call, d.precise_location); let bang = if d.is_bang { "!" } else { "" }; Diagnostic::new( "unresolved-macro-call", diff --git a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index 760f51f90498..23818d883f73 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -1,5 +1,4 @@ use hir::db::DefDatabase; -use syntax::NodeOrToken; use crate::{Diagnostic, DiagnosticsContext, Severity}; @@ -19,16 +18,7 @@ pub(crate) fn unresolved_proc_macro( proc_attr_macros_enabled: bool, ) -> Diagnostic { // Use more accurate position if available. - let display_range = (|| { - let precise_location = d.precise_location?; - let root = ctx.sema.parse_or_expand(d.node.file_id)?; - match root.covering_element(precise_location) { - NodeOrToken::Node(it) => Some(ctx.sema.original_range(&it)), - NodeOrToken::Token(it) => d.node.with_value(it).original_file_range_opt(ctx.sema.db), - } - })() - .unwrap_or_else(|| ctx.sema.diagnostics_display_range(d.node.clone())) - .range; + let display_range = ctx.resolve_precise_location(&d.node, d.precise_location); let config_enabled = match d.kind { hir::MacroKind::Attr => proc_macros_enabled && proc_attr_macros_enabled, diff --git a/crates/ide-diagnostics/src/handlers/useless_braces.rs b/crates/ide-diagnostics/src/handlers/useless_braces.rs index 8b9330e04013..289ed0458c67 100644 --- a/crates/ide-diagnostics/src/handlers/useless_braces.rs +++ b/crates/ide-diagnostics/src/handlers/useless_braces.rs @@ -71,9 +71,9 @@ use a; use a::{c, d::e}; mod a { - mod c {} - mod d { - mod e {} + pub mod c {} + pub mod d { + pub mod e {} } } "#, @@ -87,9 +87,9 @@ use a::{ }; mod a { - mod c {} - mod d { - mod e {} + pub mod c {} + pub mod d { + pub mod e {} } } "#, @@ -116,11 +116,11 @@ use b; ); check_fix( r#" -mod a { mod c {} } +mod a { pub mod c {} } use a::{c$0}; "#, r#" -mod a { mod c {} } +mod a { pub mod c {} } use a::c; "#, ); @@ -136,11 +136,11 @@ use a; ); check_fix( r#" -mod a { mod c {} mod d { mod e {} } } +mod a { pub mod c {} pub mod d { pub mod e {} } } use a::{c, d::{e$0}}; "#, r#" -mod a { mod c {} mod d { mod e {} } } +mod a { pub mod c {} pub mod d { pub mod e {} } } use a::{c, d::e}; "#, ); diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index ae299f058414..d81e36a1f863 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -182,6 +182,28 @@ struct DiagnosticsContext<'a> { resolve: &'a AssistResolveStrategy, } +impl<'a> DiagnosticsContext<'a> { + fn resolve_precise_location( + &self, + node: &InFile, + precise_location: Option, + ) -> TextRange { + let sema = &self.sema; + (|| { + let precise_location = precise_location?; + let root = sema.parse_or_expand(node.file_id)?; + match root.covering_element(precise_location) { + syntax::NodeOrToken::Node(it) => Some(sema.original_range(&it)), + syntax::NodeOrToken::Token(it) => { + node.with_value(it).original_file_range_opt(sema.db) + } + } + })() + .unwrap_or_else(|| sema.diagnostics_display_range(node.clone())) + .range + } +} + pub fn diagnostics( db: &RootDatabase, config: &DiagnosticsConfig, diff --git a/crates/ide-ssr/Cargo.toml b/crates/ide-ssr/Cargo.toml index 4baf786c4555..7be62a8d9ffe 100644 --- a/crates/ide-ssr/Cargo.toml +++ b/crates/ide-ssr/Cargo.toml @@ -5,7 +5,7 @@ description = "Structural search and replace of Rust code" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/ide/Cargo.toml b/crates/ide/Cargo.toml index 712459a7ee9c..73f202630f15 100644 --- a/crates/ide/Cargo.toml +++ b/crates/ide/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index f97c67b144ac..43f7a529bc29 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -289,10 +289,10 @@ mod b; enum E { X(Foo$0) } //- /a.rs -struct Foo; - //^^^ +pub struct Foo; + //^^^ //- /b.rs -struct Foo; +pub struct Foo; "#, ); } diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 3687b597fc64..838fb18c3d59 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs @@ -119,7 +119,14 @@ pub(crate) fn hover( }); } - let in_attr = matches!(original_token.parent().and_then(ast::TokenTree::cast), Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind()))); + let in_attr = original_token + .parent_ancestors() + .filter_map(ast::Item::cast) + .any(|item| sema.is_attr_macro_call(&item)) + && !matches!( + original_token.parent().and_then(ast::TokenTree::cast), + Some(tt) if tt.syntax().ancestors().any(|it| ast::Meta::can_cast(it.kind())) + ); // prefer descending the same token kind in attribute expansions, in normal macros text // equivalency is more important let descended = if in_attr { diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index 34d8bf67a301..37384c4e7e07 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -1,7 +1,10 @@ use std::fmt; use either::Either; -use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo}; +use hir::{ + known, Adjust, AutoBorrow, Callable, HasVisibility, HirDisplay, Mutability, OverloadedDeref, + PointerCast, Safety, Semantics, TypeInfo, +}; use ide_db::{ base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap, RootDatabase, @@ -22,7 +25,7 @@ pub struct InlayHintsConfig { pub type_hints: bool, pub parameter_hints: bool, pub chaining_hints: bool, - pub reborrow_hints: ReborrowHints, + pub adjustment_hints: AdjustmentHints, pub closure_return_type_hints: ClosureReturnTypeHints, pub binding_mode_hints: bool, pub lifetime_elision_hints: LifetimeElisionHints, @@ -48,9 +51,9 @@ pub enum LifetimeElisionHints { } #[derive(Clone, Debug, PartialEq, Eq)] -pub enum ReborrowHints { +pub enum AdjustmentHints { Always, - MutableOnly, + ReborrowOnly, Never, } @@ -61,7 +64,8 @@ pub enum InlayKind { ClosingBraceHint, ClosureReturnTypeHint, GenericParamListHint, - ImplicitReborrowHint, + AdjustmentHint, + AdjustmentHintClosingParenthesis, LifetimeHint, ParameterHint, TypeHint, @@ -115,6 +119,12 @@ impl From for InlayHintLabel { } } +impl From<&str> for InlayHintLabel { + fn from(s: &str) -> Self { + Self { parts: vec![InlayHintLabelPart { text: s.into(), linked_location: None }] } + } +} + impl fmt::Display for InlayHintLabel { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.parts.iter().map(|part| &part.text).format("")) @@ -180,7 +190,7 @@ impl fmt::Debug for InlayHintLabelPart { pub(crate) fn inlay_hints( db: &RootDatabase, file_id: FileId, - range_limit: Option, + range_limit: Option, config: &InlayHintsConfig, ) -> Vec { let _p = profile::span("inlay_hints"); @@ -195,7 +205,7 @@ pub(crate) fn inlay_hints( let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node); match range_limit { - Some(FileRange { range, .. }) => match file.covering_element(range) { + Some(range) => match file.covering_element(range) { NodeOrToken::Token(_) => return acc, NodeOrToken::Node(n) => n .descendants() @@ -221,6 +231,7 @@ fn hints( match node { ast::Expr(expr) => { chaining_hints(hints, sema, &famous_defs, config, file_id, &expr); + adjustment_hints(hints, sema, config, &expr); match expr { ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)), ast::Expr::MethodCallExpr(it) => { @@ -229,7 +240,7 @@ fn hints( ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, file_id, it), // We could show reborrows for all expressions, but usually that is just noise to the user // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it - ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), + // ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr), _ => None, } }, @@ -617,30 +628,95 @@ fn closure_ret_hints( Some(()) } -fn reborrow_hints( +fn adjustment_hints( acc: &mut Vec, sema: &Semantics<'_, RootDatabase>, config: &InlayHintsConfig, expr: &ast::Expr, ) -> Option<()> { - if config.reborrow_hints == ReborrowHints::Never { + if config.adjustment_hints == AdjustmentHints::Never { + return None; + } + + if let ast::Expr::ParenExpr(_) = expr { + // These inherit from the inner expression which would result in duplicate hints return None; } + let parent = expr.syntax().parent().and_then(ast::Expr::cast); let descended = sema.descend_node_into_attributes(expr.clone()).pop(); let desc_expr = descended.as_ref().unwrap_or(expr); - let mutability = sema.is_implicit_reborrow(desc_expr)?; - let label = match mutability { - hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*", - hir::Mutability::Mut => "&mut *", - _ => return None, + let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?; + let needs_parens = match parent { + Some(parent) => { + match parent { + ast::Expr::AwaitExpr(_) + | ast::Expr::CallExpr(_) + | ast::Expr::CastExpr(_) + | ast::Expr::FieldExpr(_) + | ast::Expr::MethodCallExpr(_) + | ast::Expr::TryExpr(_) => true, + // FIXME: shorthands need special casing, though not sure if adjustments are even valid there + ast::Expr::RecordExpr(_) => false, + ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr), + _ => false, + } + } + None => false, }; - acc.push(InlayHint { - range: expr.syntax().text_range(), - kind: InlayKind::ImplicitReborrowHint, - label: label.to_string().into(), - tooltip: Some(InlayTooltip::String("Compiler inserted reborrow".into())), - }); + if needs_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHint, + label: "(".into(), + tooltip: None, + }); + } + for adjustment in adjustments.into_iter().rev() { + // FIXME: Add some nicer tooltips to each of these + let text = match adjustment { + Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => { + "" + } + Adjust::Deref(None) => "*", + Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*", + Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*", + Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&", + Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ", + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ", + Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ", + // some of these could be represented via `as` casts, but that's not too nice and + // handling everything as a prefix expr makes the `(` and `)` insertion easier + Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => { + match cast { + PointerCast::ReifyFnPointer => "", + PointerCast::UnsafeFnPointer => "", + PointerCast::ClosureFnPointer(Safety::Unsafe) => { + "" + } + PointerCast::ClosureFnPointer(Safety::Safe) => "", + PointerCast::MutToConstPointer => "", + PointerCast::ArrayToPointer => "", + PointerCast::Unsize => "", + } + } + _ => continue, + }; + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHint, + label: text.into(), + tooltip: None, + }); + } + if needs_parens { + acc.push(InlayHint { + range: expr.syntax().text_range(), + kind: InlayKind::AdjustmentHintClosingParenthesis, + label: ")".into(), + tooltip: None, + }); + } Some(()) } @@ -1213,12 +1289,11 @@ fn get_callable( #[cfg(test)] mod tests { use expect_test::{expect, Expect}; - use ide_db::base_db::FileRange; use itertools::Itertools; use syntax::{TextRange, TextSize}; use test_utils::extract_annotations; - use crate::inlay_hints::ReborrowHints; + use crate::inlay_hints::AdjustmentHints; use crate::{fixture, inlay_hints::InlayHintsConfig, LifetimeElisionHints}; use super::ClosureReturnTypeHints; @@ -1230,7 +1305,7 @@ mod tests { chaining_hints: false, lifetime_elision_hints: LifetimeElisionHints::Never, closure_return_type_hints: ClosureReturnTypeHints::Never, - reborrow_hints: ReborrowHints::Always, + adjustment_hints: AdjustmentHints::Never, binding_mode_hints: false, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, @@ -1242,7 +1317,6 @@ mod tests { type_hints: true, parameter_hints: true, chaining_hints: true, - reborrow_hints: ReborrowHints::Always, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, binding_mode_hints: true, lifetime_elision_hints: LifetimeElisionHints::Always, @@ -1838,10 +1912,7 @@ fn main() { .inlay_hints( &InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }, file_id, - Some(FileRange { - file_id, - range: TextRange::new(TextSize::from(500), TextSize::from(600)), - }), + Some(TextRange::new(TextSize::from(500), TextSize::from(600))), ) .unwrap(); let actual = @@ -2845,48 +2916,6 @@ impl () { ); } - #[test] - fn hints_implicit_reborrow() { - check_with_config( - InlayHintsConfig { - reborrow_hints: ReborrowHints::Always, - parameter_hints: true, - ..DISABLED_CONFIG - }, - r#" -fn __() { - let unique = &mut (); - let r_mov = unique; - let foo: &mut _ = unique; - //^^^^^^ &mut * - ref_mut_id(unique); - //^^^^^^ mut_ref - //^^^^^^ &mut * - let shared = ref_id(unique); - //^^^^^^ shared_ref - //^^^^^^ &* - let mov = shared; - let r_mov: &_ = shared; - ref_id(shared); - //^^^^^^ shared_ref - - identity(unique); - identity(shared); -} -fn identity(t: T) -> T { - t -} -fn ref_mut_id(mut_ref: &mut ()) -> &mut () { - mut_ref - //^^^^^^^ &mut * -} -fn ref_id(shared_ref: &()) -> &() { - shared_ref -} -"#, - ); - } - #[test] fn hints_binding_modes() { check_with_config( @@ -2994,4 +3023,76 @@ fn f() { "#, ); } + + #[test] + fn adjustment_hints() { + check_with_config( + InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG }, + r#" +//- minicore: coerce_unsized +fn main() { + let _: u32 = loop {}; + //^^^^^^^ + let _: &u32 = &mut 0; + //^^^^^^& + //^^^^^^* + let _: &mut u32 = &mut 0; + //^^^^^^&mut $ + //^^^^^^* + let _: *const u32 = &mut 0; + //^^^^^^&raw const $ + //^^^^^^* + let _: *mut u32 = &mut 0; + //^^^^^^&raw mut $ + //^^^^^^* + let _: fn() = main; + //^^^^ + let _: unsafe fn() = main; + //^^^^ + //^^^^ + let _: unsafe fn() = main as fn(); + //^^^^^^^^^^^^ + let _: fn() = || {}; + //^^^^^ + let _: unsafe fn() = || {}; + //^^^^^ + let _: *const u32 = &mut 0u32 as *mut u32; + //^^^^^^^^^^^^^^^^^^^^^ + let _: &mut [_] = &mut [0; 0]; + //^^^^^^^^^^^ + //^^^^^^^^^^^&mut $ + //^^^^^^^^^^^* + + Struct.consume(); + Struct.by_ref(); + //^^^^^^( + //^^^^^^& + //^^^^^^) + Struct.by_ref_mut(); + //^^^^^^( + //^^^^^^&mut $ + //^^^^^^) + + (&Struct).consume(); + //^^^^^^^* + (&Struct).by_ref(); + + (&mut Struct).consume(); + //^^^^^^^^^^^* + (&mut Struct).by_ref(); + //^^^^^^^^^^^& + //^^^^^^^^^^^* + (&mut Struct).by_ref_mut(); +} + +#[derive(Copy, Clone)] +struct Struct; +impl Struct { + fn consume(self) {} + fn by_ref(&self) {} + fn by_ref_mut(&mut self) {} +} +"#, + ) + } } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 416817ca0b42..7402e86f36fa 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -81,8 +81,8 @@ pub use crate::{ highlight_related::{HighlightRelatedConfig, HighlightedRange}, hover::{HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult}, inlay_hints::{ - ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind, - InlayTooltip, LifetimeElisionHints, ReborrowHints, + AdjustmentHints, ClosureReturnTypeHints, InlayHint, InlayHintLabel, InlayHintsConfig, + InlayKind, InlayTooltip, LifetimeElisionHints, }, join_lines::JoinLinesConfig, markup::Markup, @@ -367,7 +367,7 @@ impl Analysis { &self, config: &InlayHintsConfig, file_id: FileId, - range: Option, + range: Option, ) -> Cancellable> { self.with_db(|db| inlay_hints::inlay_hints(db, file_id, range, config)) } diff --git a/crates/ide/src/moniker.rs b/crates/ide/src/moniker.rs index 07d117aff10b..fcbf6d8e58c4 100644 --- a/crates/ide/src/moniker.rs +++ b/crates/ide/src/moniker.rs @@ -1,9 +1,9 @@ //! This module generates [moniker](https://microsoft.github.io/language-server-protocol/specifications/lsif/0.6.0/specification/#exportsImports) //! for LSIF and LSP. -use hir::{db::DefDatabase, AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; +use hir::{AsAssocItem, AssocItemContainer, Crate, Name, Semantics}; use ide_db::{ - base_db::{CrateOrigin, FileId, FileLoader, FilePosition, LangCrateOrigin}, + base_db::{CrateOrigin, FilePosition, LangCrateOrigin}, defs::{Definition, IdentClass}, helpers::pick_best_token, RootDatabase, @@ -11,7 +11,7 @@ use ide_db::{ use itertools::Itertools; use syntax::{AstNode, SyntaxKind::*, T}; -use crate::{doc_links::token_as_doc_comment, RangeInfo}; +use crate::{doc_links::token_as_doc_comment, parent_module::crates_for, RangeInfo}; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum MonikerDescriptorKind { @@ -77,25 +77,13 @@ pub struct PackageInformation { pub version: Option, } -pub(crate) fn crate_for_file(db: &RootDatabase, file_id: FileId) -> Option { - for &krate in db.relevant_crates(file_id).iter() { - let crate_def_map = db.crate_def_map(krate); - for (_, data) in crate_def_map.modules() { - if data.origin.file_id() == Some(file_id) { - return Some(krate.into()); - } - } - } - None -} - pub(crate) fn moniker( db: &RootDatabase, FilePosition { file_id, offset }: FilePosition, ) -> Option>> { let sema = &Semantics::new(db); let file = sema.parse(file_id).syntax().clone(); - let current_crate = crate_for_file(db, file_id)?; + let current_crate: hir::Crate = crates_for(db, file_id).pop()?.into(); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { IDENT | INT_NUMBER diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs index e942413c1105..0f758cfa2d34 100644 --- a/crates/ide/src/references.rs +++ b/crates/ide/src/references.rs @@ -16,6 +16,7 @@ use ide_db::{ search::{ReferenceCategory, SearchScope, UsageSearchResult}, RootDatabase, }; +use itertools::Itertools; use stdx::hash::NoHashHashMap; use syntax::{ algo::find_node_at_offset, @@ -86,6 +87,7 @@ pub(crate) fn find_all_refs( file_id, refs.into_iter() .map(|file_ref| (file_ref.range, file_ref.category)) + .unique() .collect(), ) }) diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs index 7486b20293a6..e7412d27faf4 100644 --- a/crates/ide/src/signature_help.rs +++ b/crates/ide/src/signature_help.rs @@ -1345,5 +1345,36 @@ fn f i32>(f: F) { ^^ --- "#]], ); + check( + r#" +fn f &T>(f: F) { + f($0) +} +"#, + expect![[r#" + (&T, u16) -> &T + ^^ --- + "#]], + ); + } + + #[test] + fn regression_13579() { + check( + r#" +fn f() { + take(2)($0); +} + +fn take( + count: C +) -> impl Fn() -> C { + move || count +} +"#, + expect![[r#" + () -> i32 + "#]], + ); } } diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index 27ad1a948d13..2380cf7381c1 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -13,7 +13,8 @@ use syntax::{AstNode, SyntaxKind::*, SyntaxToken, TextRange, T}; use crate::{ hover::hover_for_definition, - moniker::{crate_for_file, def_to_moniker, MonikerResult}, + moniker::{def_to_moniker, MonikerResult}, + parent_module::crates_for, Analysis, Fold, HoverConfig, HoverDocFormat, HoverResult, InlayHint, InlayHintsConfig, TryToNav, }; @@ -99,7 +100,7 @@ fn all_modules(db: &dyn HirDatabase) -> Vec { impl StaticIndex<'_> { fn add_file(&mut self, file_id: FileId) { - let current_crate = crate_for_file(self.db, file_id); + let current_crate = crates_for(self.db, file_id).pop().map(Into::into); let folds = self.analysis.folding_ranges(file_id).unwrap(); let inlay_hints = self .analysis @@ -111,7 +112,7 @@ impl StaticIndex<'_> { chaining_hints: true, closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock, lifetime_elision_hints: crate::LifetimeElisionHints::Never, - reborrow_hints: crate::ReborrowHints::Never, + adjustment_hints: crate::AdjustmentHints::Never, hide_named_constructor_hints: false, hide_closure_initialization_hints: false, param_names_for_lifetime_elision_hints: false, diff --git a/crates/limit/Cargo.toml b/crates/limit/Cargo.toml index 893db436d8b7..3536f73da73e 100644 --- a/crates/limit/Cargo.toml +++ b/crates/limit/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [features] tracking = [] diff --git a/crates/mbe/Cargo.toml b/crates/mbe/Cargo.toml index 13cd8901031d..bce2fc9a70e8 100644 --- a/crates/mbe/Cargo.toml +++ b/crates/mbe/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index e4c56565b92d..cf53c16726bf 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -12,6 +12,9 @@ use tt::buffer::{Cursor, TokenBuffer}; use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap}; +#[cfg(test)] +mod tests; + /// Convert the syntax node to a `TokenTree` (what macro /// will consume). pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) { @@ -35,7 +38,7 @@ pub fn syntax_node_to_token_tree_with_modifications( append: FxHashMap>, ) -> (tt::Subtree, TokenMap, u32) { let global_offset = node.text_range().start(); - let mut c = Convertor::new(node, global_offset, existing_token_map, next_id, replace, append); + let mut c = Converter::new(node, global_offset, existing_token_map, next_id, replace, append); let subtree = convert_tokens(&mut c); c.id_alloc.map.shrink_to_fit(); always!(c.replace.is_empty(), "replace: {:?}", c.replace); @@ -100,7 +103,7 @@ pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> { return None; } - let mut conv = RawConvertor { + let mut conv = RawConverter { lexed, pos: 0, id_alloc: TokenIdAlloc { @@ -148,7 +151,7 @@ pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec { res } -fn convert_tokens(conv: &mut C) -> tt::Subtree { +fn convert_tokens(conv: &mut C) -> tt::Subtree { struct StackEntry { subtree: tt::Subtree, idx: usize, @@ -228,7 +231,7 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } let spacing = match conv.peek().map(|next| next.kind(conv)) { - Some(kind) if !kind.is_trivia() => tt::Spacing::Joint, + Some(kind) if is_single_token_op(kind) => tt::Spacing::Joint, _ => tt::Spacing::Alone, }; let char = match token.to_char(conv) { @@ -307,6 +310,35 @@ fn convert_tokens(conv: &mut C) -> tt::Subtree { } } +fn is_single_token_op(kind: SyntaxKind) -> bool { + matches!( + kind, + EQ | L_ANGLE + | R_ANGLE + | BANG + | AMP + | PIPE + | TILDE + | AT + | DOT + | COMMA + | SEMICOLON + | COLON + | POUND + | DOLLAR + | QUESTION + | PLUS + | MINUS + | STAR + | SLASH + | PERCENT + | CARET + // LIFETIME_IDENT will be split into a sequence of `'` (a single quote) and an + // identifier. + | LIFETIME_IDENT + ) +} + /// Returns the textual content of a doc comment block as a quoted string /// That is, strips leading `///` (or `/**`, etc) /// and strips the ending `*/` @@ -425,8 +457,8 @@ impl TokenIdAlloc { } } -/// A raw token (straight from lexer) convertor -struct RawConvertor<'a> { +/// A raw token (straight from lexer) converter +struct RawConverter<'a> { lexed: parser::LexedStr<'a>, pos: usize, id_alloc: TokenIdAlloc, @@ -442,7 +474,7 @@ trait SrcToken: std::fmt::Debug { fn synthetic_id(&self, ctx: &Ctx) -> Option; } -trait TokenConvertor: Sized { +trait TokenConverter: Sized { type Token: SrcToken; fn convert_doc_comment(&self, token: &Self::Token) -> Option>; @@ -454,25 +486,25 @@ trait TokenConvertor: Sized { fn id_alloc(&mut self) -> &mut TokenIdAlloc; } -impl<'a> SrcToken> for usize { - fn kind(&self, ctx: &RawConvertor<'a>) -> SyntaxKind { +impl<'a> SrcToken> for usize { + fn kind(&self, ctx: &RawConverter<'a>) -> SyntaxKind { ctx.lexed.kind(*self) } - fn to_char(&self, ctx: &RawConvertor<'a>) -> Option { + fn to_char(&self, ctx: &RawConverter<'a>) -> Option { ctx.lexed.text(*self).chars().next() } - fn to_text(&self, ctx: &RawConvertor<'_>) -> SmolStr { + fn to_text(&self, ctx: &RawConverter<'_>) -> SmolStr { ctx.lexed.text(*self).into() } - fn synthetic_id(&self, _ctx: &RawConvertor<'a>) -> Option { + fn synthetic_id(&self, _ctx: &RawConverter<'a>) -> Option { None } } -impl<'a> TokenConvertor for RawConvertor<'a> { +impl<'a> TokenConverter for RawConverter<'a> { type Token = usize; fn convert_doc_comment(&self, &token: &usize) -> Option> { @@ -504,7 +536,7 @@ impl<'a> TokenConvertor for RawConvertor<'a> { } } -struct Convertor { +struct Converter { id_alloc: TokenIdAlloc, current: Option, current_synthetic: Vec, @@ -515,7 +547,7 @@ struct Convertor { punct_offset: Option<(SyntaxToken, TextSize)>, } -impl Convertor { +impl Converter { fn new( node: &SyntaxNode, global_offset: TextSize, @@ -523,11 +555,11 @@ impl Convertor { next_id: u32, mut replace: FxHashMap>, mut append: FxHashMap>, - ) -> Convertor { + ) -> Converter { let range = node.text_range(); let mut preorder = node.preorder_with_tokens(); let (first, synthetic) = Self::next_token(&mut preorder, &mut replace, &mut append); - Convertor { + Converter { id_alloc: { TokenIdAlloc { map: existing_token_map, global_offset, next_id } }, current: first, current_synthetic: synthetic, @@ -590,15 +622,15 @@ impl SynToken { } } -impl SrcToken for SynToken { - fn kind(&self, _ctx: &Convertor) -> SyntaxKind { +impl SrcToken for SynToken { + fn kind(&self, ctx: &Converter) -> SyntaxKind { match self { SynToken::Ordinary(token) => token.kind(), - SynToken::Punch(token, _) => token.kind(), + SynToken::Punch(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(), SynToken::Synthetic(token) => token.kind, } } - fn to_char(&self, _ctx: &Convertor) -> Option { + fn to_char(&self, _ctx: &Converter) -> Option { match self { SynToken::Ordinary(_) => None, SynToken::Punch(it, i) => it.text().chars().nth((*i).into()), @@ -606,7 +638,7 @@ impl SrcToken for SynToken { SynToken::Synthetic(_) => None, } } - fn to_text(&self, _ctx: &Convertor) -> SmolStr { + fn to_text(&self, _ctx: &Converter) -> SmolStr { match self { SynToken::Ordinary(token) => token.text().into(), SynToken::Punch(token, _) => token.text().into(), @@ -614,7 +646,7 @@ impl SrcToken for SynToken { } } - fn synthetic_id(&self, _ctx: &Convertor) -> Option { + fn synthetic_id(&self, _ctx: &Converter) -> Option { match self { SynToken::Synthetic(token) => Some(token.id), _ => None, @@ -622,7 +654,7 @@ impl SrcToken for SynToken { } } -impl TokenConvertor for Convertor { +impl TokenConverter for Converter { type Token = SynToken; fn convert_doc_comment(&self, token: &Self::Token) -> Option> { convert_doc_comment(token.token()?) @@ -651,7 +683,7 @@ impl TokenConvertor for Convertor { } let curr = self.current.clone()?; - if !&self.range.contains_range(curr.text_range()) { + if !self.range.contains_range(curr.text_range()) { return None; } let (new_current, new_synth) = @@ -809,12 +841,15 @@ impl<'a> TtTreeSink<'a> { let next = last.bump(); if let ( Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(curr), _)), - Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(_), _)), + Some(tt::buffer::TokenTreeRef::Leaf(tt::Leaf::Punct(next), _)), ) = (last.token_tree(), next.token_tree()) { // Note: We always assume the semi-colon would be the last token in // other parts of RA such that we don't add whitespace here. - if curr.spacing == tt::Spacing::Alone && curr.char != ';' { + // + // When `next` is a `Punct` of `'`, that's a part of a lifetime identifier so we don't + // need to add whitespace either. + if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' { self.inner.token(WHITESPACE, " "); self.text_pos += TextSize::of(' '); } diff --git a/crates/mbe/src/syntax_bridge/tests.rs b/crates/mbe/src/syntax_bridge/tests.rs new file mode 100644 index 000000000000..4e04d2bc1c77 --- /dev/null +++ b/crates/mbe/src/syntax_bridge/tests.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; + +use syntax::{ast, AstNode}; +use test_utils::extract_annotations; +use tt::{ + buffer::{TokenBuffer, TokenTreeRef}, + Leaf, Punct, Spacing, +}; + +use super::syntax_node_to_token_tree; + +fn check_punct_spacing(fixture: &str) { + let source_file = ast::SourceFile::parse(fixture).ok().unwrap(); + let (subtree, token_map) = syntax_node_to_token_tree(source_file.syntax()); + let mut annotations: HashMap<_, _> = extract_annotations(fixture) + .into_iter() + .map(|(range, annotation)| { + let token = token_map.token_by_range(range).expect("no token found"); + let spacing = match annotation.as_str() { + "Alone" => Spacing::Alone, + "Joint" => Spacing::Joint, + a => panic!("unknown annotation: {}", a), + }; + (token, spacing) + }) + .collect(); + + let buf = TokenBuffer::from_subtree(&subtree); + let mut cursor = buf.begin(); + while !cursor.eof() { + while let Some(token_tree) = cursor.token_tree() { + if let TokenTreeRef::Leaf(Leaf::Punct(Punct { spacing, id, .. }), _) = token_tree { + if let Some(expected) = annotations.remove(&id) { + assert_eq!(expected, *spacing); + } + } + cursor = cursor.bump_subtree(); + } + cursor = cursor.bump(); + } + + assert!(annotations.is_empty(), "unchecked annotations: {:?}", annotations); +} + +#[test] +fn punct_spacing() { + check_punct_spacing( + r#" +fn main() { + 0+0; + //^ Alone + 0+(0); + //^ Alone + 0<=0; + //^ Joint + // ^ Alone + 0<=(0); + // ^ Alone + a=0; + //^ Alone + a=(0); + //^ Alone + a+=0; + //^ Joint + // ^ Alone + a+=(0); + // ^ Alone + a&&b; + //^ Joint + // ^ Alone + a&&(b); + // ^ Alone + foo::bar; + // ^ Joint + // ^ Alone + use foo::{bar,baz,}; + // ^ Alone + // ^ Alone + // ^ Alone + struct Struct<'a> {}; + // ^ Joint + // ^ Joint + Struct::<0>; + // ^ Alone + Struct::<{0}>; + // ^ Alone + ;; + //^ Joint + // ^ Alone +} + "#, + ); +} diff --git a/crates/parser/Cargo.toml b/crates/parser/Cargo.toml index a286a6bcddde..d1420de8937a 100644 --- a/crates/parser/Cargo.toml +++ b/crates/parser/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/paths/Cargo.toml b/crates/paths/Cargo.toml index 5e83de7d994e..d23a63d2a973 100644 --- a/crates/paths/Cargo.toml +++ b/crates/paths/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/proc-macro-api/Cargo.toml b/crates/proc-macro-api/Cargo.toml index 54879c1870c0..f261f3def45d 100644 --- a/crates/proc-macro-api/Cargo.toml +++ b/crates/proc-macro-api/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index 9d0da5dee9c1..7991e125ab83 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [dependencies] proc-macro-srv = { version = "0.0.0", path = "../proc-macro-srv" } diff --git a/crates/proc-macro-srv/Cargo.toml b/crates/proc-macro-srv/Cargo.toml index e39026ac70bf..a136abc12b75 100644 --- a/crates/proc-macro-srv/Cargo.toml +++ b/crates/proc-macro-srv/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs index 2f854bc15954..0ce099ae0bab 100644 --- a/crates/proc-macro-srv/src/abis/mod.rs +++ b/crates/proc-macro-srv/src/abis/mod.rs @@ -117,7 +117,7 @@ impl Abi { let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?; Ok(Abi::Abi1_63(inner)) } - _ => Err(LoadProcMacroDylibError::UnsupportedABI), + _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string.clone())), } } diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 7aba74e5396d..0722cd89d729 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -80,14 +80,14 @@ fn load_library(file: &Path) -> Result { pub enum LoadProcMacroDylibError { Io(io::Error), LibLoading(libloading::Error), - UnsupportedABI, + UnsupportedABI(String), } impl fmt::Display for LoadProcMacroDylibError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Io(e) => e.fmt(f), - Self::UnsupportedABI => write!(f, "unsupported ABI version"), + Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"), Self::LibLoading(e) => e.fmt(f), } } diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index 72a2dfe72d37..b4f5ebd157f3 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -113,12 +113,12 @@ impl ProcMacroSrv { fn expander(&mut self, path: &Path) -> Result<&dylib::Expander, String> { let time = fs::metadata(path).and_then(|it| it.modified()).map_err(|err| { - format!("Failed to get file metadata for {}: {:?}", path.display(), err) + format!("Failed to get file metadata for {}: {}", path.display(), err) })?; Ok(match self.expanders.entry((path.to_path_buf(), time)) { Entry::Vacant(v) => v.insert(dylib::Expander::new(path).map_err(|err| { - format!("Cannot create expander for {}: {:?}", path.display(), err) + format!("Cannot create expander for {}: {}", path.display(), err) })?), Entry::Occupied(e) => e.into_mut(), }) diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs index b46cdddcf6b1..cc0fc91fe989 100644 --- a/crates/proc-macro-srv/src/tests/mod.rs +++ b/crates/proc-macro-srv/src/tests/mod.rs @@ -19,7 +19,7 @@ fn test_derive_error() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [joint] 4294967295 + PUNCH ! [alone] 4294967295 SUBTREE () 4294967295 LITERAL "#[derive(DeriveError)] struct S ;" 4294967295 PUNCH ; [alone] 4294967295"##]], @@ -109,7 +109,7 @@ fn test_fn_like_macro_clone_literals() { PUNCH , [alone] 4294967295 LITERAL 2_u32 4294967295 PUNCH , [alone] 4294967295 - PUNCH - [joint] 4294967295 + PUNCH - [alone] 4294967295 LITERAL 4i64 4294967295 PUNCH , [alone] 4294967295 LITERAL 3.14f32 4294967295 @@ -130,7 +130,7 @@ fn test_attr_macro() { expect![[r##" SUBTREE $ IDENT compile_error 4294967295 - PUNCH ! [joint] 4294967295 + PUNCH ! [alone] 4294967295 SUBTREE () 4294967295 LITERAL "#[attr_error(some arguments)] mod m {}" 4294967295 PUNCH ; [alone] 4294967295"##]], diff --git a/crates/proc-macro-test/Cargo.toml b/crates/proc-macro-test/Cargo.toml index 684477191b27..d2a79f91074a 100644 --- a/crates/proc-macro-test/Cargo.toml +++ b/crates/proc-macro-test/Cargo.toml @@ -3,7 +3,7 @@ name = "proc-macro-test" version = "0.0.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" publish = false [lib] diff --git a/crates/proc-macro-test/imp/Cargo.toml b/crates/proc-macro-test/imp/Cargo.toml index 2d1fc3c5c7a3..1bd14070e90d 100644 --- a/crates/proc-macro-test/imp/Cargo.toml +++ b/crates/proc-macro-test/imp/Cargo.toml @@ -3,7 +3,7 @@ name = "proc-macro-test-impl" version = "0.0.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" publish = false [lib] diff --git a/crates/profile/Cargo.toml b/crates/profile/Cargo.toml index 5697aea964f7..01d1735bf784 100644 --- a/crates/profile/Cargo.toml +++ b/crates/profile/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/project-model/Cargo.toml b/crates/project-model/Cargo.toml index cf9868740cb0..39902a53214d 100644 --- a/crates/project-model/Cargo.toml +++ b/crates/project-model/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs index a26a7c57acfc..ae2b41f27d58 100644 --- a/crates/project-model/src/build_scripts.rs +++ b/crates/project-model/src/build_scripts.rs @@ -69,7 +69,7 @@ impl WorkspaceBuildScripts { cmd.args(&["check", "--quiet", "--workspace", "--message-format=json"]); // --all-targets includes tests, benches and examples in addition to the - // default lib and bins. This is an independent concept from the --targets + // default lib and bins. This is an independent concept from the --target // flag below. cmd.arg("--all-targets"); diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index b4c2ba436772..02ec7a4f6f99 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -270,11 +270,7 @@ impl CargoWorkspace { config: &CargoConfig, progress: &dyn Fn(String), ) -> Result { - let target = config - .target - .clone() - .or_else(|| cargo_config_build_target(cargo_toml, &config.extra_env)) - .or_else(|| rustc_discover_host_triple(cargo_toml, &config.extra_env)); + let targets = find_list_of_build_targets(config, cargo_toml); let mut meta = MetadataCommand::new(); meta.cargo_path(toolchain::cargo()); @@ -294,8 +290,12 @@ impl CargoWorkspace { } meta.current_dir(current_dir.as_os_str()); - if let Some(target) = target { - meta.other_options(vec![String::from("--filter-platform"), target]); + if !targets.is_empty() { + let other_options: Vec<_> = targets + .into_iter() + .flat_map(|target| ["--filter-platform".to_string(), target]) + .collect(); + meta.other_options(other_options); } // FIXME: Fetching metadata is a slow process, as it might require @@ -469,6 +469,19 @@ impl CargoWorkspace { } } +fn find_list_of_build_targets(config: &CargoConfig, cargo_toml: &ManifestPath) -> Vec { + if let Some(target) = &config.target { + return [target.into()].to_vec(); + } + + let build_targets = cargo_config_build_target(cargo_toml, &config.extra_env); + if !build_targets.is_empty() { + return build_targets; + } + + rustc_discover_host_triple(cargo_toml, &config.extra_env).into_iter().collect() +} + fn rustc_discover_host_triple( cargo_toml: &ManifestPath, extra_env: &FxHashMap, @@ -499,7 +512,7 @@ fn rustc_discover_host_triple( fn cargo_config_build_target( cargo_toml: &ManifestPath, extra_env: &FxHashMap, -) -> Option { +) -> Vec { let mut cargo_config = Command::new(toolchain::cargo()); cargo_config.envs(extra_env); cargo_config @@ -507,12 +520,21 @@ fn cargo_config_build_target( .args(&["-Z", "unstable-options", "config", "get", "build.target"]) .env("RUSTC_BOOTSTRAP", "1"); // if successful we receive `build.target = "target-triple"` + // or `build.target = ["", ..]` tracing::debug!("Discovering cargo config target by {:?}", cargo_config); - match utf8_stdout(cargo_config) { - Ok(stdout) => stdout - .strip_prefix("build.target = \"") - .and_then(|stdout| stdout.strip_suffix('"')) - .map(ToOwned::to_owned), - Err(_) => None, + utf8_stdout(cargo_config).map(parse_output_cargo_config_build_target).unwrap_or_default() +} + +fn parse_output_cargo_config_build_target(stdout: String) -> Vec { + let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"'); + + if !trimmed.starts_with('[') { + return [trimmed.to_string()].to_vec(); + } + + let res = serde_json::from_str(trimmed); + if let Err(e) = &res { + tracing::warn!("Failed to parse `build.target` as an array of target: {}`", e); } + res.unwrap_or_default() } diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index fa8d76f3f452..f6c09a27c9d7 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -128,14 +128,18 @@ impl Sysroot { } if let Some(alloc) = sysroot.by_name("alloc") { - if let Some(core) = sysroot.by_name("core") { - sysroot.crates[alloc].deps.push(core); + for dep in ALLOC_DEPS.trim().lines() { + if let Some(dep) = sysroot.by_name(dep) { + sysroot.crates[alloc].deps.push(dep) + } } } if let Some(proc_macro) = sysroot.by_name("proc_macro") { - if let Some(std) = sysroot.by_name("std") { - sysroot.crates[proc_macro].deps.push(std); + for dep in PROC_MACRO_DEPS.trim().lines() { + if let Some(dep) = sysroot.by_name(dep) { + sysroot.crates[proc_macro].deps.push(dep) + } } } @@ -239,6 +243,7 @@ fn get_rust_src(sysroot_path: &AbsPath) -> Option { const SYSROOT_CRATES: &str = " alloc +backtrace core panic_abort panic_unwind @@ -246,17 +251,19 @@ proc_macro profiler_builtins std stdarch/crates/std_detect -term test unwind"; +const ALLOC_DEPS: &str = "core"; + const STD_DEPS: &str = " alloc -core -panic_abort panic_unwind +panic_abort +core profiler_builtins +unwind std_detect -term -test -unwind"; +test"; + +const PROC_MACRO_DEPS: &str = "std"; diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs index e2444e24974a..a1cb438bddc4 100644 --- a/crates/project-model/src/tests.rs +++ b/crates/project-model/src/tests.rs @@ -1566,10 +1566,10 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 1, + 3, ), name: CrateName( - "core", + "panic_unwind", ), prelude: true, }, @@ -1584,10 +1584,10 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 3, + 1, ), name: CrateName( - "panic_unwind", + "core", ), prelude: true, }, @@ -1602,40 +1602,31 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 7, + 9, ), name: CrateName( - "std_detect", + "unwind", ), prelude: true, }, Dependency { crate_id: CrateId( - 8, + 7, ), name: CrateName( - "term", + "std_detect", ), prelude: true, }, Dependency { crate_id: CrateId( - 9, + 8, ), name: CrateName( "test", ), prelude: true, }, - Dependency { - crate_id: CrateId( - 10, - ), - name: CrateName( - "unwind", - ), - prelude: true, - }, ], proc_macro: Err( "no proc macro loaded for sysroot crate", @@ -1687,40 +1678,6 @@ fn rust_project_hello_world_project_model() { ), edition: Edition2018, version: None, - display_name: Some( - CrateDisplayName { - crate_name: CrateName( - "term", - ), - canonical_name: "term", - }, - ), - cfg_options: CfgOptions( - [], - ), - potential_cfg_options: CfgOptions( - [], - ), - env: Env { - entries: {}, - }, - dependencies: [], - proc_macro: Err( - "no proc macro loaded for sysroot crate", - ), - origin: Lang( - Other, - ), - is_proc_macro: false, - }, - CrateId( - 9, - ): CrateData { - root_file_id: FileId( - 10, - ), - edition: Edition2018, - version: None, display_name: Some( CrateDisplayName { crate_name: CrateName( @@ -1748,10 +1705,10 @@ fn rust_project_hello_world_project_model() { is_proc_macro: false, }, CrateId( - 10, + 9, ): CrateData { root_file_id: FileId( - 11, + 10, ), edition: Edition2018, version: None, @@ -1782,10 +1739,10 @@ fn rust_project_hello_world_project_model() { is_proc_macro: false, }, CrateId( - 11, + 10, ): CrateData { root_file_id: FileId( - 12, + 11, ), edition: Edition2018, version: None, @@ -1836,7 +1793,7 @@ fn rust_project_hello_world_project_model() { }, Dependency { crate_id: CrateId( - 9, + 8, ), name: CrateName( "test", diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 2780c62ed118..3d199ed24afe 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -377,6 +377,21 @@ impl ProjectWorkspace { } } + pub fn find_sysroot_proc_macro_srv(&self) -> Option { + match self { + ProjectWorkspace::Cargo { sysroot: Some(sysroot), .. } + | ProjectWorkspace::Json { sysroot: Some(sysroot), .. } => { + let standalone_server_name = + format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); + ["libexec", "lib"] + .into_iter() + .map(|segment| sysroot.root().join(segment).join(&standalone_server_name)) + .find(|server_path| std::fs::metadata(&server_path).is_ok()) + } + _ => None, + } + } + /// Returns the roots for the current `ProjectWorkspace` /// The return type contains the path and whether or not /// the root is a member of the current workspace @@ -509,14 +524,14 @@ impl ProjectWorkspace { build_scripts, toolchain: _, } => cargo_to_crate_graph( - rustc_cfg.clone(), - cfg_overrides, load_proc_macro, load, + rustc, cargo, - build_scripts, sysroot.as_ref(), - rustc, + rustc_cfg.clone(), + cfg_overrides, + build_scripts, ), ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => { detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot) @@ -602,7 +617,7 @@ fn project_json_to_crate_graph( for (from, krate) in project.crates() { if let Some(&from) = crates.get(&from) { if let Some((public_deps, libproc_macro)) = &sysroot_deps { - public_deps.add(from, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, from); if krate.is_proc_macro { if let Some(proc_macro) = libproc_macro { add_dep( @@ -626,14 +641,14 @@ fn project_json_to_crate_graph( } fn cargo_to_crate_graph( - rustc_cfg: Vec, - override_cfg: &CfgOverrides, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, load: &mut dyn FnMut(&AbsPath) -> Option, + rustc: &Option, cargo: &CargoWorkspace, - build_scripts: &WorkspaceBuildScripts, sysroot: Option<&Sysroot>, - rustc: &Option, + rustc_cfg: Vec, + override_cfg: &CfgOverrides, + build_scripts: &WorkspaceBuildScripts, ) -> CrateGraph { let _p = profile::span("cargo_to_crate_graph"); let mut crate_graph = CrateGraph::default(); @@ -642,13 +657,15 @@ fn cargo_to_crate_graph( None => (SysrootPublicDeps::default(), None), }; - let mut cfg_options = CfgOptions::default(); - cfg_options.extend(rustc_cfg); + let cfg_options = { + let mut cfg_options = CfgOptions::default(); + cfg_options.extend(rustc_cfg); + cfg_options.insert_atom("debug_assertions".into()); + cfg_options + }; let mut pkg_to_lib_crate = FxHashMap::default(); - cfg_options.insert_atom("debug_assertions".into()); - let mut pkg_crates = FxHashMap::default(); // Does any crate signal to rust-analyzer that they need the rustc_private crates? let mut has_private = false; @@ -723,7 +740,7 @@ fn cargo_to_crate_graph( // Set deps to the core, std and to the lib target of the current package for &(from, kind) in pkg_crates.get(&pkg).into_iter().flatten() { // Add sysroot deps first so that a lib target named `core` etc. can overwrite them. - public_deps.add(from, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, from); if let Some((to, name)) = lib_tgt.clone() { if to != from && kind != TargetKind::BuildScript { @@ -767,15 +784,16 @@ fn cargo_to_crate_graph( if let Some(rustc_workspace) = rustc { handle_rustc_crates( &mut crate_graph, - rustc_workspace, + &mut pkg_to_lib_crate, load, - &cfg_options, - override_cfg, load_proc_macro, - &mut pkg_to_lib_crate, - &public_deps, + rustc_workspace, cargo, + &public_deps, + libproc_macro, &pkg_crates, + &cfg_options, + override_cfg, build_scripts, ); } @@ -825,28 +843,29 @@ fn detached_files_to_crate_graph( }, ); - public_deps.add(detached_file_crate, &mut crate_graph); + public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate); } crate_graph } fn handle_rustc_crates( crate_graph: &mut CrateGraph, - rustc_workspace: &CargoWorkspace, + pkg_to_lib_crate: &mut FxHashMap, load: &mut dyn FnMut(&AbsPath) -> Option, - cfg_options: &CfgOptions, - override_cfg: &CfgOverrides, load_proc_macro: &mut dyn FnMut(&str, &AbsPath) -> ProcMacroLoadResult, - pkg_to_lib_crate: &mut FxHashMap, - public_deps: &SysrootPublicDeps, + rustc_workspace: &CargoWorkspace, cargo: &CargoWorkspace, + public_deps: &SysrootPublicDeps, + libproc_macro: Option, pkg_crates: &FxHashMap>, + cfg_options: &CfgOptions, + override_cfg: &CfgOverrides, build_scripts: &WorkspaceBuildScripts, ) { let mut rustc_pkg_crates = FxHashMap::default(); // The root package of the rustc-dev component is rustc_driver, so we match that let root_pkg = - rustc_workspace.packages().find(|package| rustc_workspace[*package].name == "rustc_driver"); + rustc_workspace.packages().find(|&package| rustc_workspace[package].name == "rustc_driver"); // The rustc workspace might be incomplete (such as if rustc-dev is not // installed for the current toolchain) and `rustc_source` is set to discover. if let Some(root_pkg) = root_pkg { @@ -901,7 +920,16 @@ fn handle_rustc_crates( ); pkg_to_lib_crate.insert(pkg, crate_id); // Add dependencies on core / std / alloc for this crate - public_deps.add(crate_id, crate_graph); + public_deps.add_to_crate_graph(crate_graph, crate_id); + if let Some(proc_macro) = libproc_macro { + add_dep_with_prelude( + crate_graph, + crate_id, + CrateName::new("proc_macro").unwrap(), + proc_macro, + rustc_workspace[tgt].is_proc_macro, + ); + } rustc_pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id); } } @@ -1009,7 +1037,7 @@ struct SysrootPublicDeps { impl SysrootPublicDeps { /// Makes `from` depend on the public sysroot crates. - fn add(&self, from: CrateId, crate_graph: &mut CrateGraph) { + fn add_to_crate_graph(&self, crate_graph: &mut CrateGraph, from: CrateId) { for (name, krate, prelude) in &self.deps { add_dep_with_prelude(crate_graph, from, name.clone(), *krate, *prelude); } diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 7ae5324ab051..56f14fe18749 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -8,7 +8,7 @@ documentation = "https://rust-analyzer.github.io/manual.html" license = "MIT OR Apache-2.0" autobins = false edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/rust-analyzer/src/cargo_target_spec.rs b/crates/rust-analyzer/src/cargo_target_spec.rs index 6ede194babc2..cf51cf15a0e1 100644 --- a/crates/rust-analyzer/src/cargo_target_spec.rs +++ b/crates/rust-analyzer/src/cargo_target_spec.rs @@ -3,11 +3,11 @@ use std::mem; use cfg::{CfgAtom, CfgExpr}; -use ide::{FileId, RunnableKind, TestId}; +use ide::{Cancellable, FileId, RunnableKind, TestId}; use project_model::{self, CargoFeatures, ManifestPath, TargetKind}; use vfs::AbsPathBuf; -use crate::{global_state::GlobalStateSnapshot, Result}; +use crate::global_state::GlobalStateSnapshot; /// Abstract representation of Cargo target. /// @@ -29,7 +29,7 @@ impl CargoTargetSpec { spec: Option, kind: &RunnableKind, cfg: &Option, - ) -> Result<(Vec, Vec)> { + ) -> (Vec, Vec) { let mut args = Vec::new(); let mut extra_args = Vec::new(); @@ -111,13 +111,13 @@ impl CargoTargetSpec { } } } - Ok((args, extra_args)) + (args, extra_args) } pub(crate) fn for_file( global_state_snapshot: &GlobalStateSnapshot, file_id: FileId, - ) -> Result> { + ) -> Cancellable> { let crate_id = match &*global_state_snapshot.analysis.crates_for(file_id)? { &[crate_id, ..] => crate_id, _ => return Ok(None), diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 5dba545b8718..762d7d3a18e8 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -60,24 +60,12 @@ pub fn load_workspace( }; let proc_macro_client = if load_config.with_proc_macro { - let mut path = AbsPathBuf::assert(std::env::current_exe()?); - let mut args = vec!["proc-macro"]; - - if let ProjectWorkspace::Cargo { sysroot, .. } | ProjectWorkspace::Json { sysroot, .. } = - &ws - { - if let Some(sysroot) = sysroot.as_ref() { - let standalone_server_name = - format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); - let server_path = sysroot.root().join("libexec").join(&standalone_server_name); - if std::fs::metadata(&server_path).is_ok() { - path = server_path; - args = vec![]; - } - } - } + let (server_path, args): (_, &[_]) = match ws.find_sysroot_proc_macro_srv() { + Some(server_path) => (server_path, &[]), + None => (AbsPathBuf::assert(std::env::current_exe()?), &["proc-macro"]), + }; - ProcMacroServer::spawn(path.clone(), args.clone()).map_err(|e| e.to_string()) + ProcMacroServer::spawn(server_path, args).map_err(|e| e.to_string()) } else { Err("proc macro server disabled".to_owned()) }; diff --git a/crates/rust-analyzer/src/cli/scip.rs b/crates/rust-analyzer/src/cli/scip.rs index ca7ba896b67c..9edd045ab071 100644 --- a/crates/rust-analyzer/src/cli/scip.rs +++ b/crates/rust-analyzer/src/cli/scip.rs @@ -47,30 +47,27 @@ impl flags::Scip { let si = StaticIndex::compute(&analysis); - let mut index = scip_types::Index { - metadata: Some(scip_types::Metadata { - version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), - tool_info: Some(scip_types::ToolInfo { - name: "rust-analyzer".to_owned(), - version: "0.1".to_owned(), - arguments: vec![], - ..Default::default() - }) - .into(), - project_root: format!( - "file://{}", - path.normalize() - .as_os_str() - .to_str() - .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))? - .to_string() - ), - text_document_encoding: scip_types::TextEncoding::UTF8.into(), - ..Default::default() + let metadata = scip_types::Metadata { + version: scip_types::ProtocolVersion::UnspecifiedProtocolVersion.into(), + tool_info: Some(scip_types::ToolInfo { + name: "rust-analyzer".to_owned(), + version: "0.1".to_owned(), + arguments: vec![], + special_fields: Default::default(), }) .into(), - ..Default::default() + project_root: format!( + "file://{}", + path.normalize() + .as_os_str() + .to_str() + .ok_or(anyhow::anyhow!("Unable to normalize project_root path"))? + .to_string() + ), + text_document_encoding: scip_types::TextEncoding::UTF8.into(), + special_fields: Default::default(), }; + let mut documents = Vec::new(); let mut symbols_emitted: HashSet = HashSet::default(); let mut tokens_to_symbol: HashMap = HashMap::new(); @@ -95,18 +92,14 @@ impl flags::Scip { endings: LineEndings::Unix, }; - let mut doc = scip_types::Document { - relative_path, - language: "rust".to_string(), - ..Default::default() - }; + let mut occurrences = Vec::new(); + let mut symbols = Vec::new(); - tokens.into_iter().for_each(|(range, id)| { + tokens.into_iter().for_each(|(text_range, id)| { let token = si.tokens.get(id).unwrap(); - let mut occurrence = scip_types::Occurrence::default(); - occurrence.range = text_range_to_scip_range(&line_index, range); - occurrence.symbol = tokens_to_symbol + let range = text_range_to_scip_range(&line_index, text_range); + let symbol = tokens_to_symbol .entry(id) .or_insert_with(|| { let symbol = token_to_symbol(&token).unwrap_or_else(&mut new_local_symbol); @@ -114,34 +107,62 @@ impl flags::Scip { }) .clone(); + let mut symbol_roles = Default::default(); + if let Some(def) = token.definition { - if def.range == range { - occurrence.symbol_roles |= scip_types::SymbolRole::Definition as i32; + if def.range == text_range { + symbol_roles |= scip_types::SymbolRole::Definition as i32; } if symbols_emitted.insert(id) { - let mut symbol_info = scip_types::SymbolInformation::default(); - symbol_info.symbol = occurrence.symbol.clone(); - if let Some(hover) = &token.hover { - if !hover.markup.as_str().is_empty() { - symbol_info.documentation = vec![hover.markup.as_str().to_string()]; - } - } - - doc.symbols.push(symbol_info) + let documentation = token + .hover + .as_ref() + .map(|hover| hover.markup.as_str()) + .filter(|it| !it.is_empty()) + .map(|it| vec![it.to_owned()]); + let symbol_info = scip_types::SymbolInformation { + symbol: symbol.clone(), + documentation: documentation.unwrap_or_default(), + relationships: Vec::new(), + special_fields: Default::default(), + }; + + symbols.push(symbol_info) } } - doc.occurrences.push(occurrence); + occurrences.push(scip_types::Occurrence { + range, + symbol, + symbol_roles, + override_documentation: Vec::new(), + syntax_kind: Default::default(), + diagnostics: Vec::new(), + special_fields: Default::default(), + }); }); - if doc.occurrences.is_empty() { + if occurrences.is_empty() { continue; } - index.documents.push(doc); + documents.push(scip_types::Document { + relative_path, + language: "rust".to_string(), + occurrences, + symbols, + special_fields: Default::default(), + }); } + let index = scip_types::Index { + metadata: Some(metadata).into(), + documents, + external_symbols: Vec::new(), + special_fields: Default::default(), + }; + scip::write_message_to_file("index.scip", index) .map_err(|err| anyhow::anyhow!("Failed to write scip to file: {}", err))?; @@ -181,7 +202,7 @@ fn new_descriptor_str( name: name.to_string(), disambiguator: "".to_string(), suffix: suffix.into(), - ..Default::default() + special_fields: Default::default(), } } @@ -232,11 +253,11 @@ fn token_to_symbol(token: &TokenStaticData) -> Option { manager: "cargo".to_string(), name: package_name, version: version.unwrap_or_else(|| ".".to_string()), - ..Default::default() + special_fields: Default::default(), }) .into(), descriptors, - ..Default::default() + special_fields: Default::default(), }) } diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 4072ae585dbd..6b2f22faa717 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -118,9 +118,11 @@ config_data! { /// This option does not take effect until rust-analyzer is restarted. cargo_sysroot: Option = "\"discover\"", /// 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 = "null", /// Unsets `#[cfg(test)]` for the specified crates. - cargo_unsetTest: Vec = "[\"core\"]", + cargo_unsetTest: Vec = "[\"core\"]", /// Check all targets and tests (`--all-targets`). checkOnSave_allTargets: bool = "true", @@ -157,7 +159,7 @@ config_data! { checkOnSave_noDefaultFeatures: Option = "null", /// Override the command rust-analyzer uses instead of `cargo check` for /// diagnostics on save. The command is required to output json and - /// should therefor include `--message-format=json` or a similar option. + /// should therefore include `--message-format=json` or a similar option. /// /// If you're changing this because you're using some tool wrapping /// Cargo, you might also want to change @@ -174,9 +176,13 @@ config_data! { /// ``` /// . checkOnSave_overrideCommand: Option> = "null", - /// Check for a specific target. Defaults to - /// `#rust-analyzer.cargo.target#`. - checkOnSave_target: Option = "null", + /// 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"`. + checkOnSave_target | checkOnSave_targets: CheckOnSaveTargets = "[]", /// 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. @@ -261,6 +267,7 @@ config_data! { files_excludeDirs: Vec = "[]", /// Controls file watching implementation. files_watcher: 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 exit points while the cursor is on any `return`, `?`, `fn`, or return type arrow (`->`). @@ -320,6 +327,8 @@ config_data! { inlayHints_closingBraceHints_minLines: usize = "25", /// Whether to show inlay type hints for return types of closures. inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef = "\"never\"", + /// Whether to show inlay hints for type adjustments. + inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"", /// Whether to show inlay type hints for elided lifetimes in function signatures. inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", /// Whether to prefer using parameter names as the name for elided lifetime hints if possible. @@ -329,7 +338,8 @@ config_data! { /// Whether to show function parameter name inlay hints at the call /// site. inlayHints_parameterHints_enable: bool = "true", - /// Whether to show inlay type hints for compiler inserted reborrows. + /// 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 = "\"never\"", /// Whether to render leading colons for type hints, and trailing colons for parameter hints. inlayHints_renderColons: bool = "true", @@ -1143,11 +1153,10 @@ impl Config { } Some(_) | None => FlycheckConfig::CargoCommand { command: self.data.checkOnSave_command.clone(), - target_triple: self - .data - .checkOnSave_target - .clone() - .or_else(|| self.data.cargo_target.clone()), + target_triples: match &self.data.checkOnSave_target.0[..] { + [] => self.data.cargo_target.clone().into_iter().collect(), + targets => targets.into(), + }, all_targets: self.data.checkOnSave_allTargets, no_default_features: self .data @@ -1200,10 +1209,15 @@ impl Config { hide_closure_initialization_hints: self .data .inlayHints_typeHints_hideClosureInitialization, - reborrow_hints: match self.data.inlayHints_reborrowHints_enable { - ReborrowHintsDef::Always => ide::ReborrowHints::Always, - ReborrowHintsDef::Never => ide::ReborrowHints::Never, - ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly, + adjustment_hints: match self.data.inlayHints_expressionAdjustmentHints_enable { + AdjustmentHintsDef::Always => ide::AdjustmentHints::Always, + AdjustmentHintsDef::Never => match self.data.inlayHints_reborrowHints_enable { + ReborrowHintsDef::Always | ReborrowHintsDef::Mutable => { + ide::AdjustmentHints::ReborrowOnly + } + ReborrowHintsDef::Never => ide::AdjustmentHints::Never, + }, + AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly, }, binding_mode_hints: self.data.inlayHints_bindingModeHints_enable, param_names_for_lifetime_elision_hints: self @@ -1538,6 +1552,7 @@ mod de_unit_v { named_unit_variant!(all); named_unit_variant!(skip_trivial); named_unit_variant!(mutable); + named_unit_variant!(reborrow); named_unit_variant!(with_block); } @@ -1647,6 +1662,9 @@ enum InvocationStrategy { PerWorkspace, } +#[derive(Deserialize, Debug, Clone)] +struct CheckOnSaveTargets(#[serde(deserialize_with = "single_or_array")] Vec); + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum InvocationLocation { @@ -1687,6 +1705,17 @@ enum ReborrowHintsDef { Mutable, } +#[derive(Deserialize, Debug, Clone)] +#[serde(untagged)] +enum AdjustmentHintsDef { + #[serde(deserialize_with = "true_or_always")] + Always, + #[serde(deserialize_with = "false_or_never")] + Never, + #[serde(deserialize_with = "de_unit_v::reborrow")] + Reborrow, +} + #[derive(Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum FilesWatcherDef { @@ -1996,6 +2025,19 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "Only show mutable reborrow hints." ] }, + "AdjustmentHintsDef" => set! { + "type": "string", + "enum": [ + "always", + "never", + "reborrow" + ], + "enumDescriptions": [ + "Always show all adjustment hints.", + "Never show adjustment hints.", + "Only show auto borrow and dereference adjustment hints." + ] + }, "CargoFeaturesDef" => set! { "anyOf": [ { @@ -2084,6 +2126,17 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "The command will be executed in the project root." ], }, + "CheckOnSaveTargets" => set! { + "anyOf": [ + { + "type": "string", + }, + { + "type": "array", + "items": { "type": "string" } + }, + ], + }, _ => panic!("missing entry for {}: {}", ty, default), } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 189ac2fbf533..beb23c54c9f0 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -359,14 +359,15 @@ pub(crate) fn map_rust_diagnostic_to_lsp( .iter() .flat_map(|primary_span| { let primary_location = primary_location(config, workspace_root, primary_span, snap); - - let mut message = message.clone(); - if needs_primary_span_label { - if let Some(primary_span_label) = &primary_span.label { - format_to!(message, "\n{}", primary_span_label); + let message = { + let mut message = message.clone(); + if needs_primary_span_label { + if let Some(primary_span_label) = &primary_span.label { + format_to!(message, "\n{}", primary_span_label); + } } - } - + message + }; // Each primary diagnostic span may result in multiple LSP diagnostics. let mut diagnostics = Vec::new(); @@ -417,7 +418,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( message: message.clone(), related_information: Some(information_for_additional_diagnostic), tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, + data: Some(serde_json::json!({ "rendered": rd.rendered })), }; diagnostics.push(MappedRustDiagnostic { url: secondary_location.uri, @@ -449,7 +450,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp( } }, tags: if tags.is_empty() { None } else { Some(tags.clone()) }, - data: None, + data: Some(serde_json::json!({ "rendered": rd.rendered })), }, fix: None, }); @@ -534,7 +535,8 @@ mod tests { Config::new(workspace_root.to_path_buf(), ClientCapabilities::default()), ); let snap = state.snapshot(); - let actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap); + let mut actual = map_rust_diagnostic_to_lsp(&config, &diagnostic, workspace_root, &snap); + actual.iter_mut().for_each(|diag| diag.diagnostic.data = None); expect.assert_debug_eq(&actual) } diff --git a/crates/rust-analyzer/src/from_proto.rs b/crates/rust-analyzer/src/from_proto.rs index 936957bab488..dd433b0f4d31 100644 --- a/crates/rust-analyzer/src/from_proto.rs +++ b/crates/rust-analyzer/src/from_proto.rs @@ -42,8 +42,10 @@ pub(crate) fn offset(line_index: &LineIndex, position: lsp_types::Position) -> R pub(crate) fn text_range(line_index: &LineIndex, range: lsp_types::Range) -> Result { let start = offset(line_index, range.start)?; let end = offset(line_index, range.end)?; - let text_range = TextRange::new(start, end); - Ok(text_range) + match end < start { + true => Err(format_err!("Invalid Range").into()), + false => Ok(TextRange::new(start, end)), + } } pub(crate) fn file_id(snap: &GlobalStateSnapshot, url: &lsp_types::Url) -> Result { diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index 74277ff2e576..4e8bc8d6462c 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -100,7 +100,7 @@ pub(crate) struct GlobalState { /// the user just adds comments or whitespace to Cargo.toml, we do not want /// to invalidate any salsa caches. pub(crate) workspaces: Arc>, - pub(crate) fetch_workspaces_queue: OpQueue>>, + pub(crate) fetch_workspaces_queue: OpQueue>>>, pub(crate) fetch_build_data_queue: OpQueue<(Arc>, Vec>)>, diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 34795a8eb40a..d190a9f4e2ca 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -9,9 +9,9 @@ use std::{ use anyhow::Context; use ide::{ - AnnotationConfig, AssistKind, AssistResolveStrategy, FileId, FilePosition, FileRange, - HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, RunnableKind, - SingleResolve, SourceChange, TextEdit, + AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FileId, FilePosition, + FileRange, HoverAction, HoverGotoTypeData, Query, RangeInfo, ReferenceCategory, Runnable, + RunnableKind, SingleResolve, SourceChange, TextEdit, }; use ide_db::SymbolKind; use lsp_server::ErrorCode; @@ -556,7 +556,7 @@ pub(crate) fn handle_will_rename_files( if source_change.source_file_edits.is_empty() { Ok(None) } else { - to_proto::workspace_edit(&snap, source_change).map(Some) + Ok(Some(to_proto::workspace_edit(&snap, source_change)?)) } } @@ -1313,7 +1313,7 @@ pub(crate) fn handle_ssr( position, selections, )??; - to_proto::workspace_edit(&snap, source_change) + to_proto::workspace_edit(&snap, source_change).map_err(Into::into) } pub(crate) fn publish_diagnostics( @@ -1354,13 +1354,12 @@ pub(crate) fn handle_inlay_hints( ) -> Result>> { let _p = profile::span("handle_inlay_hints"); let document_uri = ¶ms.text_document.uri; - let file_id = from_proto::file_id(&snap, document_uri)?; - let line_index = snap.file_line_index(file_id)?; - let range = from_proto::file_range( + let FileRange { file_id, range } = from_proto::file_range( &snap, TextDocumentIdentifier::new(document_uri.to_owned()), params.range, )?; + let line_index = snap.file_line_index(file_id)?; let inlay_hints_config = snap.config.inlay_hints(); Ok(Some( snap.analysis @@ -1369,7 +1368,7 @@ pub(crate) fn handle_inlay_hints( .map(|it| { to_proto::inlay_hint(&snap, &line_index, inlay_hints_config.render_colons, it) }) - .collect::>>()?, + .collect::>>()?, )) } @@ -1426,7 +1425,7 @@ pub(crate) fn handle_call_hierarchy_prepare( .into_iter() .filter(|it| it.kind == Some(SymbolKind::Function)) .map(|it| to_proto::call_hierarchy_item(&snap, it)) - .collect::>>()?; + .collect::>>()?; Ok(Some(res)) } diff --git a/crates/rust-analyzer/src/line_index.rs b/crates/rust-analyzer/src/line_index.rs index 0d424b915703..2945dba12f25 100644 --- a/crates/rust-analyzer/src/line_index.rs +++ b/crates/rust-analyzer/src/line_index.rs @@ -27,10 +27,6 @@ pub(crate) enum LineEndings { impl LineEndings { /// Replaces `\r\n` with `\n` in-place in `src`. pub(crate) fn normalize(src: String) -> (String, LineEndings) { - if !src.as_bytes().contains(&b'\r') { - return (src, LineEndings::Unix); - } - // We replace `\r\n` with `\n` in-place, which doesn't break utf-8 encoding. // While we *can* call `as_mut_vec` and do surgery on the live string // directly, let's rather steal the contents of `src`. This makes the code @@ -39,10 +35,19 @@ impl LineEndings { let mut buf = src.into_bytes(); let mut gap_len = 0; let mut tail = buf.as_mut_slice(); + let mut crlf_seen = false; + + let find_crlf = |src: &[u8]| src.windows(2).position(|it| it == b"\r\n"); + loop { let idx = match find_crlf(&tail[gap_len..]) { - None => tail.len(), - Some(idx) => idx + gap_len, + None if crlf_seen => tail.len(), + // SAFETY: buf is unchanged and therefore still contains utf8 data + None => return (unsafe { String::from_utf8_unchecked(buf) }, LineEndings::Unix), + Some(idx) => { + crlf_seen = true; + idx + gap_len + } }; tail.copy_within(gap_len..idx, 0); tail = &mut tail[idx - gap_len..]; @@ -54,15 +59,48 @@ impl LineEndings { // Account for removed `\r`. // After `set_len`, `buf` is guaranteed to contain utf-8 again. - let new_len = buf.len() - gap_len; let src = unsafe { + let new_len = buf.len() - gap_len; buf.set_len(new_len); String::from_utf8_unchecked(buf) }; - return (src, LineEndings::Dos); + (src, LineEndings::Dos) + } +} - fn find_crlf(src: &[u8]) -> Option { - src.windows(2).position(|it| it == b"\r\n") - } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unix() { + let src = "a\nb\nc\n\n\n\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Unix); + assert_eq!(res, src); + } + + #[test] + fn dos() { + let src = "\r\na\r\n\r\nb\r\nc\r\n\r\n\r\n\r\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Dos); + assert_eq!(res, "\na\n\nb\nc\n\n\n\n"); + } + + #[test] + fn mixed() { + let src = "a\r\nb\r\nc\r\n\n\r\n\n"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Dos); + assert_eq!(res, "a\nb\nc\n\n\n\n"); + } + + #[test] + fn none() { + let src = "abc"; + let (res, endings) = LineEndings::normalize(src.into()); + assert_eq!(endings, LineEndings::Unix); + assert_eq!(res, src); } } diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index c6a4db9a453a..0971dc36f3a5 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -1,5 +1,5 @@ //! Utilities for LSP-related boilerplate code. -use std::{ops::Range, sync::Arc}; +use std::{mem, ops::Range, sync::Arc}; use lsp_server::Notification; @@ -133,11 +133,37 @@ impl GlobalState { } pub(crate) fn apply_document_changes( - old_text: &mut String, - content_changes: Vec, -) { + file_contents: impl FnOnce() -> String, + mut content_changes: Vec, +) -> String { + // Skip to the last full document change, as it invalidates all previous changes anyways. + let mut start = content_changes + .iter() + .rev() + .position(|change| change.range.is_none()) + .map(|idx| content_changes.len() - idx - 1) + .unwrap_or(0); + + let mut text: String = match content_changes.get_mut(start) { + // peek at the first content change as an optimization + Some(lsp_types::TextDocumentContentChangeEvent { range: None, text, .. }) => { + let text = mem::take(text); + start += 1; + + // The only change is a full document update + if start == content_changes.len() { + return text; + } + text + } + Some(_) => file_contents(), + // we received no content changes + None => return file_contents(), + }; + let mut line_index = LineIndex { - index: Arc::new(ide::LineIndex::new(old_text)), + // the index will be overwritten in the bottom loop's first iteration + index: Arc::new(ide::LineIndex::new(&text)), // We don't care about line endings or offset encoding here. endings: LineEndings::Unix, encoding: PositionEncoding::Utf16, @@ -148,38 +174,20 @@ pub(crate) fn apply_document_changes( // Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we // remember the last valid line in the index and only rebuild it if needed. // The VFS will normalize the end of lines to `\n`. - enum IndexValid { - All, - UpToLineExclusive(u32), - } - - impl IndexValid { - fn covers(&self, line: u32) -> bool { - match *self { - IndexValid::UpToLineExclusive(to) => to > line, - _ => true, - } - } - } - - let mut index_valid = IndexValid::All; + let mut index_valid = !0u32; for change in content_changes { - match change.range { - Some(range) => { - if !index_valid.covers(range.end.line) { - line_index.index = Arc::new(ide::LineIndex::new(old_text)); - } - index_valid = IndexValid::UpToLineExclusive(range.start.line); - if let Ok(range) = from_proto::text_range(&line_index, range) { - old_text.replace_range(Range::::from(range), &change.text); - } + // The None case can't happen as we have handled it above already + if let Some(range) = change.range { + if index_valid <= range.end.line { + *Arc::make_mut(&mut line_index.index) = ide::LineIndex::new(&text); } - None => { - *old_text = change.text; - index_valid = IndexValid::UpToLineExclusive(0); + index_valid = range.start.line; + if let Ok(range) = from_proto::text_range(&line_index, range) { + text.replace_range(Range::::from(range), &change.text); } } } + text } /// Checks that the edits inside the completion and the additional edits do not overlap. @@ -242,11 +250,10 @@ mod tests { }; } - let mut text = String::new(); - apply_document_changes(&mut text, vec![]); + let text = apply_document_changes(|| String::new(), vec![]); assert_eq!(text, ""); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, vec![TextDocumentContentChangeEvent { range: None, range_length: None, @@ -254,39 +261,39 @@ mod tests { }], ); assert_eq!(text, "the"); - apply_document_changes(&mut text, c![0, 3; 0, 3 => " quick"]); + let text = apply_document_changes(|| text, c![0, 3; 0, 3 => " quick"]); assert_eq!(text, "the quick"); - apply_document_changes(&mut text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); + let text = apply_document_changes(|| text, c![0, 0; 0, 4 => "", 0, 5; 0, 5 => " foxes"]); assert_eq!(text, "quick foxes"); - apply_document_changes(&mut text, c![0, 11; 0, 11 => "\ndream"]); + let text = apply_document_changes(|| text, c![0, 11; 0, 11 => "\ndream"]); assert_eq!(text, "quick foxes\ndream"); - apply_document_changes(&mut text, c![1, 0; 1, 0 => "have "]); + let text = apply_document_changes(|| text, c![1, 0; 1, 0 => "have "]); assert_eq!(text, "quick foxes\nhave dream"); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, c![0, 0; 0, 0 => "the ", 1, 4; 1, 4 => " quiet", 1, 16; 1, 16 => "s\n"], ); assert_eq!(text, "the quick foxes\nhave quiet dreams\n"); - apply_document_changes(&mut text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); + let text = apply_document_changes(|| text, c![0, 15; 0, 15 => "\n", 2, 17; 2, 17 => "\n"]); assert_eq!(text, "the quick foxes\n\nhave quiet dreams\n\n"); - apply_document_changes( - &mut text, + let text = apply_document_changes( + || text, c![1, 0; 1, 0 => "DREAM", 2, 0; 2, 0 => "they ", 3, 0; 3, 0 => "DON'T THEY?"], ); assert_eq!(text, "the quick foxes\nDREAM\nthey have quiet dreams\nDON'T THEY?\n"); - apply_document_changes(&mut text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); + let text = apply_document_changes(|| text, c![0, 10; 1, 5 => "", 2, 0; 2, 12 => ""]); assert_eq!(text, "the quick \nthey have quiet dreams\n"); - text = String::from("❤️"); - apply_document_changes(&mut text, c![0, 0; 0, 0 => "a"]); + let text = String::from("❤️"); + let text = apply_document_changes(|| text, c![0, 0; 0, 0 => "a"]); assert_eq!(text, "a❤️"); - text = String::from("a\nb"); - apply_document_changes(&mut text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); + let text = String::from("a\nb"); + let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "\nțc", 0, 1; 1, 1 => "d"]); assert_eq!(text, "adcb"); - text = String::from("a\nb"); - apply_document_changes(&mut text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); + let text = String::from("a\nb"); + let text = apply_document_changes(|| text, c![0, 1; 1, 0 => "ț\nc", 0, 2; 0, 2 => "c"]); assert_eq!(text, "ațc\ncb"); } diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 7d10dc5d15b6..274588ce0e07 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -451,7 +451,7 @@ impl GlobalState { ProjectWorkspaceProgress::Begin => (Progress::Begin, None), ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)), ProjectWorkspaceProgress::End(workspaces) => { - self.fetch_workspaces_queue.op_completed(workspaces); + self.fetch_workspaces_queue.op_completed(Some(workspaces)); let old = Arc::clone(&self.workspaces); self.switch_workspaces("fetched workspace".to_string()); @@ -759,8 +759,10 @@ impl GlobalState { let vfs = &mut this.vfs.write().0; let file_id = vfs.file_id(&path).unwrap(); - let mut text = String::from_utf8(vfs.file_contents(file_id).to_vec()).unwrap(); - apply_document_changes(&mut text, params.content_changes); + let text = apply_document_changes( + || std::str::from_utf8(vfs.file_contents(file_id)).unwrap().into(), + params.content_changes, + ); vfs.set_file_contents(path, Some(text.into_bytes())); } diff --git a/crates/rust-analyzer/src/mem_docs.rs b/crates/rust-analyzer/src/mem_docs.rs index f86a0f66ad8d..45a1dab9772f 100644 --- a/crates/rust-analyzer/src/mem_docs.rs +++ b/crates/rust-analyzer/src/mem_docs.rs @@ -7,7 +7,7 @@ use vfs::VfsPath; /// Holds the set of in-memory documents. /// -/// For these document, there true contents is maintained by the client. It +/// For these document, their true contents is maintained by the client. It /// might be different from what's on disk. #[derive(Default, Clone)] pub(crate) struct MemDocs { @@ -19,6 +19,7 @@ impl MemDocs { pub(crate) fn contains(&self, path: &VfsPath) -> bool { self.mem_docs.contains_key(path) } + pub(crate) fn insert(&mut self, path: VfsPath, data: DocumentData) -> Result<(), ()> { self.added_or_removed = true; match self.mem_docs.insert(path, data) { @@ -26,6 +27,7 @@ impl MemDocs { None => Ok(()), } } + pub(crate) fn remove(&mut self, path: &VfsPath) -> Result<(), ()> { self.added_or_removed = true; match self.mem_docs.remove(path) { @@ -33,17 +35,21 @@ impl MemDocs { None => Err(()), } } + pub(crate) fn get(&self, path: &VfsPath) -> Option<&DocumentData> { self.mem_docs.get(path) } + pub(crate) fn get_mut(&mut self, path: &VfsPath) -> Option<&mut DocumentData> { // NB: don't set `self.added_or_removed` here, as that purposefully only // tracks changes to the key set. self.mem_docs.get_mut(path) } + pub(crate) fn iter(&self) -> impl Iterator { self.mem_docs.keys() } + pub(crate) fn take_changes(&mut self) -> bool { mem::replace(&mut self.added_or_removed, false) } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index e1f651786dee..fcfe4be0b8ce 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -106,6 +106,14 @@ impl GlobalState { status.health = lsp_ext::Health::Error; status.message = Some(error) } + + if self.config.linked_projects().is_empty() + && self.config.detached_files().is_empty() + && self.config.notifications().cargo_toml_not_found + { + status.health = lsp_ext::Health::Warning; + status.message = Some("Workspace reload required".to_string()) + } status } @@ -198,12 +206,9 @@ impl GlobalState { self.show_and_log_error("failed to run build scripts".to_string(), Some(error)); } - let workspaces = self - .fetch_workspaces_queue - .last_op_result() - .iter() - .filter_map(|res| res.as_ref().ok().cloned()) - .collect::>(); + let Some(workspaces) = self.fetch_workspaces_queue.last_op_result() else { return; }; + let workspaces = + workspaces.iter().filter_map(|res| res.as_ref().ok().cloned()).collect::>(); fn eq_ignore_build_data<'a>( left: &'a ProjectWorkspace, @@ -300,9 +305,6 @@ impl GlobalState { let files_config = self.config.files(); let project_folders = ProjectFolders::new(&self.workspaces, &files_config.exclude); - let standalone_server_name = - format!("rust-analyzer-proc-macro-srv{}", std::env::consts::EXE_SUFFIX); - if self.proc_macro_clients.is_empty() { if let Some((path, path_manually_set)) = self.config.proc_macro_srv() { tracing::info!("Spawning proc-macro servers"); @@ -310,40 +312,17 @@ impl GlobalState { .workspaces .iter() .map(|ws| { - let (path, args) = if path_manually_set { + let (path, args): (_, &[_]) = if path_manually_set { tracing::debug!( "Pro-macro server path explicitly set: {}", path.display() ); - (path.clone(), vec![]) + (path.clone(), &[]) } else { - let mut sysroot_server = None; - if let ProjectWorkspace::Cargo { sysroot, .. } - | ProjectWorkspace::Json { sysroot, .. } = ws - { - if let Some(sysroot) = sysroot.as_ref() { - let server_path = sysroot - .root() - .join("libexec") - .join(&standalone_server_name); - if std::fs::metadata(&server_path).is_ok() { - tracing::debug!( - "Sysroot proc-macro server exists at {}", - server_path.display() - ); - sysroot_server = Some(server_path); - } else { - tracing::debug!( - "Sysroot proc-macro server does not exist at {}", - server_path.display() - ); - } - } + match ws.find_sysroot_proc_macro_srv() { + Some(server_path) => (server_path, &[]), + None => (path.clone(), &["proc-macro"]), } - sysroot_server.map_or_else( - || (path.clone(), vec!["proc-macro".to_owned()]), - |path| (path, vec![]), - ) }; tracing::info!(?args, "Using proc-macro server at {}", path.display(),); @@ -427,9 +406,14 @@ impl GlobalState { fn fetch_workspace_error(&self) -> Result<(), String> { let mut buf = String::new(); - for ws in self.fetch_workspaces_queue.last_op_result() { - if let Err(err) = ws { - stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); + let Some(last_op_result) = self.fetch_workspaces_queue.last_op_result() else { return Ok(()) }; + if last_op_result.is_empty() { + stdx::format_to!(buf, "rust-analyzer failed to discover workspace"); + } else { + for ws in last_op_result { + if let Err(err) = ws { + stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err); + } } } diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs index 6c84a2069cd5..81cc1952ba5c 100644 --- a/crates/rust-analyzer/src/to_proto.rs +++ b/crates/rust-analyzer/src/to_proto.rs @@ -24,7 +24,7 @@ use crate::{ line_index::{LineEndings, LineIndex, PositionEncoding}, lsp_ext, lsp_utils::invalid_params_error, - semantic_tokens, Result, + semantic_tokens, }; pub(crate) fn position(line_index: &LineIndex, offset: TextSize) -> lsp_types::Position { @@ -429,7 +429,7 @@ pub(crate) fn inlay_hint( line_index: &LineIndex, render_colons: bool, mut inlay_hint: InlayHint, -) -> Result { +) -> Cancellable { match inlay_hint.kind { InlayKind::ParameterHint if render_colons => inlay_hint.label.append_str(":"), InlayKind::TypeHint if render_colons => inlay_hint.label.prepend_str(": "), @@ -440,32 +440,35 @@ pub(crate) fn inlay_hint( Ok(lsp_types::InlayHint { position: match inlay_hint.kind { // before annotated thing - InlayKind::ParameterHint - | InlayKind::ImplicitReborrowHint - | InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()), + InlayKind::ParameterHint | InlayKind::AdjustmentHint | InlayKind::BindingModeHint => { + position(line_index, inlay_hint.range.start()) + } // after annotated thing InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::GenericParamListHint + | InlayKind::AdjustmentHintClosingParenthesis | InlayKind::LifetimeHint | InlayKind::ClosingBraceHint => position(line_index, inlay_hint.range.end()), }, padding_left: Some(match inlay_hint.kind { InlayKind::TypeHint => !render_colons, InlayKind::ChainingHint | InlayKind::ClosingBraceHint => true, - InlayKind::BindingModeHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::BindingModeHint | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::LifetimeHint | InlayKind::ParameterHint => false, }), padding_right: Some(match inlay_hint.kind { - InlayKind::ChainingHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint | InlayKind::GenericParamListHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::TypeHint | InlayKind::ClosingBraceHint => false, InlayKind::BindingModeHint => inlay_hint.label.as_simple_str() != Some("&"), @@ -476,10 +479,11 @@ pub(crate) fn inlay_hint( InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => { Some(lsp_types::InlayHintKind::TYPE) } - InlayKind::BindingModeHint + InlayKind::AdjustmentHintClosingParenthesis + | InlayKind::BindingModeHint | InlayKind::GenericParamListHint | InlayKind::LifetimeHint - | InlayKind::ImplicitReborrowHint + | InlayKind::AdjustmentHint | InlayKind::ClosingBraceHint => None, }, text_edits: None, @@ -518,7 +522,7 @@ pub(crate) fn inlay_hint( fn inlay_hint_label( snap: &GlobalStateSnapshot, label: InlayHintLabel, -) -> Result { +) -> Cancellable { Ok(match label.as_simple_str() { Some(s) => lsp_types::InlayHintLabel::String(s.into()), None => lsp_types::InlayHintLabel::LabelParts( @@ -536,7 +540,7 @@ fn inlay_hint_label( command: None, }) }) - .collect::>>()?, + .collect::>>()?, ), }) } @@ -794,7 +798,7 @@ pub(crate) fn optional_versioned_text_document_identifier( pub(crate) fn location( snap: &GlobalStateSnapshot, frange: FileRange, -) -> Result { +) -> Cancellable { let url = url(snap, frange.file_id); let line_index = snap.file_line_index(frange.file_id)?; let range = range(&line_index, frange.range); @@ -806,7 +810,7 @@ pub(crate) fn location( pub(crate) fn location_from_nav( snap: &GlobalStateSnapshot, nav: NavigationTarget, -) -> Result { +) -> Cancellable { let url = url(snap, nav.file_id); let line_index = snap.file_line_index(nav.file_id)?; let range = range(&line_index, nav.full_range); @@ -818,7 +822,7 @@ pub(crate) fn location_link( snap: &GlobalStateSnapshot, src: Option, target: NavigationTarget, -) -> Result { +) -> Cancellable { let origin_selection_range = match src { Some(src) => { let line_index = snap.file_line_index(src.file_id)?; @@ -840,7 +844,7 @@ pub(crate) fn location_link( fn location_info( snap: &GlobalStateSnapshot, target: NavigationTarget, -) -> Result<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { +) -> Cancellable<(lsp_types::Url, lsp_types::Range, lsp_types::Range)> { let line_index = snap.file_line_index(target.file_id)?; let target_uri = url(snap, target.file_id); @@ -854,12 +858,12 @@ pub(crate) fn goto_definition_response( snap: &GlobalStateSnapshot, src: Option, targets: Vec, -) -> Result { +) -> Cancellable { if snap.config.location_link() { let links = targets .into_iter() .map(|nav| location_link(snap, src, nav)) - .collect::>>()?; + .collect::>>()?; Ok(links.into()) } else { let locations = targets @@ -867,7 +871,7 @@ pub(crate) fn goto_definition_response( .map(|nav| { location(snap, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }) }) - .collect::>>()?; + .collect::>>()?; Ok(locations.into()) } } @@ -881,7 +885,7 @@ pub(crate) fn snippet_text_document_edit( is_snippet: bool, file_id: FileId, edit: TextEdit, -) -> Result { +) -> Cancellable { let text_document = optional_versioned_text_document_identifier(snap, file_id); let line_index = snap.file_line_index(file_id)?; let mut edits: Vec<_> = @@ -958,7 +962,7 @@ pub(crate) fn snippet_text_document_ops( pub(crate) fn snippet_workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, -) -> Result { +) -> Cancellable { let mut document_changes: Vec = Vec::new(); for op in source_change.file_system_edits { @@ -995,7 +999,7 @@ pub(crate) fn snippet_workspace_edit( pub(crate) fn workspace_edit( snap: &GlobalStateSnapshot, source_change: SourceChange, -) -> Result { +) -> Cancellable { assert!(!source_change.is_snippet); snippet_workspace_edit(snap, source_change).map(|it| it.into()) } @@ -1048,7 +1052,7 @@ impl From pub(crate) fn call_hierarchy_item( snap: &GlobalStateSnapshot, target: NavigationTarget, -) -> Result { +) -> Cancellable { let name = target.name.to_string(); let detail = target.description.clone(); let kind = target.kind.map(symbol_kind).unwrap_or(lsp_types::SymbolKind::FUNCTION); @@ -1080,7 +1084,7 @@ pub(crate) fn code_action( snap: &GlobalStateSnapshot, assist: Assist, resolve_data: Option<(usize, lsp_types::CodeActionParams)>, -) -> Result { +) -> Cancellable { let mut res = lsp_ext::CodeAction { title: assist.label.to_string(), group: assist.group.filter(|_| snap.config.code_action_group()).map(|gr| gr.0), @@ -1113,13 +1117,13 @@ pub(crate) fn code_action( pub(crate) fn runnable( snap: &GlobalStateSnapshot, runnable: Runnable, -) -> Result { +) -> Cancellable { let config = snap.config.runnables(); let spec = CargoTargetSpec::for_file(snap, runnable.nav.file_id)?; let workspace_root = spec.as_ref().map(|it| it.workspace_root.clone()); let target = spec.as_ref().map(|s| s.target.clone()); let (cargo_args, executable_args) = - CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg)?; + CargoTargetSpec::runnable_args(snap, spec, &runnable.kind, &runnable.cfg); let label = runnable.label(target); let location = location_link(snap, None, runnable.nav)?; @@ -1142,7 +1146,7 @@ pub(crate) fn code_lens( acc: &mut Vec, snap: &GlobalStateSnapshot, annotation: Annotation, -) -> Result<()> { +) -> Cancellable<()> { let client_commands_config = snap.config.client_commands(); match annotation.kind { AnnotationKind::Runnable(run) => { diff --git a/crates/sourcegen/Cargo.toml b/crates/sourcegen/Cargo.toml index e75867e2d81c..593dc4e55b21 100644 --- a/crates/sourcegen/Cargo.toml +++ b/crates/sourcegen/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/stdx/Cargo.toml b/crates/stdx/Cargo.toml index e0657ab0f6d1..f7b7d09640ff 100644 --- a/crates/stdx/Cargo.toml +++ b/crates/stdx/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false @@ -16,7 +16,7 @@ always-assert = { version = "0.1.2", features = ["log"] } # Think twice before adding anything here [target.'cfg(windows)'.dependencies] -miow = "0.4.0" +miow = "0.5.0" winapi = { version = "0.3.9", features = ["winerror"] } [features] diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 1ef903371cf8..00743cca5593 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -5,7 +5,7 @@ description = "Comment and whitespace preserving parser for the Rust language" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/syntax/fuzz/Cargo.toml b/crates/syntax/fuzz/Cargo.toml index ba2f515b0bc1..f295c40065db 100644 --- a/crates/syntax/fuzz/Cargo.toml +++ b/crates/syntax/fuzz/Cargo.toml @@ -4,7 +4,7 @@ name = "syntax-fuzz" version = "0.0.1" publish = false edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [package.metadata] cargo-fuzz = true diff --git a/crates/syntax/rust.ungram b/crates/syntax/rust.ungram index 5379732ac6c3..0a0cb0290d6c 100644 --- a/crates/syntax/rust.ungram +++ b/crates/syntax/rust.ungram @@ -239,8 +239,11 @@ Static = Trait = Attr* Visibility? 'unsafe'? 'auto'? - 'trait' Name GenericParamList? (':' TypeBoundList?)? WhereClause? - AssocItemList + 'trait' Name GenericParamList? + ( + (':' TypeBoundList?)? WhereClause? AssocItemList + | '=' TypeBoundList? WhereClause? ';' + ) AssocItemList = '{' Attr* AssocItem* '}' diff --git a/crates/syntax/src/ast/generated/nodes.rs b/crates/syntax/src/ast/generated/nodes.rs index 6cfb98d92fcf..2ea715f47fb2 100644 --- a/crates/syntax/src/ast/generated/nodes.rs +++ b/crates/syntax/src/ast/generated/nodes.rs @@ -407,6 +407,8 @@ impl Trait { pub fn auto_token(&self) -> Option { support::token(&self.syntax, T![auto]) } pub fn trait_token(&self) -> Option { support::token(&self.syntax, T![trait]) } pub fn assoc_item_list(&self) -> Option { support::child(&self.syntax) } + pub fn eq_token(&self) -> Option { support::token(&self.syntax, T![=]) } + pub fn semicolon_token(&self) -> Option { support::token(&self.syntax, T![;]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index ba72e64425b2..8990f7a7d4e8 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs @@ -209,17 +209,19 @@ impl ast::String { let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let mut buf = String::new(); - let mut text_iter = text.chars(); + let mut prev_end = 0; let mut has_error = false; unescape_literal(text, Mode::Str, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { (Ok(c), false) => buf.push(c), - (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } (Ok(c), true) => { buf.reserve_exact(text.len()); - buf.push_str(&text[..char_range.start]); + buf.push_str(&text[..prev_end]); buf.push(c); } (Err(_), _) => has_error = true, @@ -252,17 +254,19 @@ impl ast::ByteString { let text = &text[self.text_range_between_quotes()? - self.syntax().text_range().start()]; let mut buf: Vec = Vec::new(); - let mut text_iter = text.chars(); + let mut prev_end = 0; let mut has_error = false; unescape_literal(text, Mode::ByteStr, &mut |char_range, unescaped_char| match ( unescaped_char, buf.capacity() == 0, ) { (Ok(c), false) => buf.push(c as u8), - (Ok(c), true) if char_range.len() == 1 && Some(c) == text_iter.next() => (), + (Ok(_), true) if char_range.len() == 1 && char_range.start == prev_end => { + prev_end = char_range.end + } (Ok(c), true) => { buf.reserve_exact(text.len()); - buf.extend_from_slice(text[..char_range.start].as_bytes()); + buf.extend_from_slice(text[..prev_end].as_bytes()); buf.push(c as u8); } (Err(_), _) => has_error = true, @@ -445,6 +449,36 @@ mod tests { check_string_value(r"\foobar", None); check_string_value(r"\nfoobar", "\nfoobar"); check_string_value(r"C:\\Windows\\System32\\", "C:\\Windows\\System32\\"); + check_string_value(r"\x61bcde", "abcde"); + check_string_value( + r"a\ +bcde", "abcde", + ); + } + + fn check_byte_string_value<'a, const N: usize>( + lit: &str, + expected: impl Into>, + ) { + assert_eq!( + ast::ByteString { syntax: make::tokens::literal(&format!("b\"{}\"", lit)) } + .value() + .as_deref(), + expected.into().map(|value| &value[..]) + ); + } + + #[test] + fn test_byte_string_escape() { + check_byte_string_value(r"foobar", b"foobar"); + check_byte_string_value(r"\foobar", None::<&[u8; 0]>); + check_byte_string_value(r"\nfoobar", b"\nfoobar"); + check_byte_string_value(r"C:\\Windows\\System32\\", b"C:\\Windows\\System32\\"); + check_byte_string_value(r"\x61bcde", b"abcde"); + check_byte_string_value( + r"a\ +bcde", b"abcde", + ); } #[test] diff --git a/crates/syntax/src/tests/sourcegen_ast.rs b/crates/syntax/src/tests/sourcegen_ast.rs index 70b54843dbaa..712ef5f63b65 100644 --- a/crates/syntax/src/tests/sourcegen_ast.rs +++ b/crates/syntax/src/tests/sourcegen_ast.rs @@ -86,7 +86,7 @@ fn generate_nodes(kinds: KindsSrc<'_>, grammar: &AstSrc) -> String { .traits .iter() .filter(|trait_name| { - // Loops have two expressions so this might collide, therefor manual impl it + // Loops have two expressions so this might collide, therefore manual impl it node.name != "ForExpr" && node.name != "WhileExpr" || trait_name.as_str() != "HasLoopBody" }) diff --git a/crates/test-utils/Cargo.toml b/crates/test-utils/Cargo.toml index cceafe04e377..1047373b1c75 100644 --- a/crates/test-utils/Cargo.toml +++ b/crates/test-utils/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/text-edit/Cargo.toml b/crates/text-edit/Cargo.toml index 7a90d64a98ba..8df7e1af6116 100644 --- a/crates/text-edit/Cargo.toml +++ b/crates/text-edit/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/toolchain/Cargo.toml b/crates/toolchain/Cargo.toml index 3e0f31f19c50..a6a3ae742aeb 100644 --- a/crates/toolchain/Cargo.toml +++ b/crates/toolchain/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/tt/Cargo.toml b/crates/tt/Cargo.toml index 52dfb8608041..4f2103f3a97f 100644 --- a/crates/tt/Cargo.toml +++ b/crates/tt/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/vfs-notify/Cargo.toml b/crates/vfs-notify/Cargo.toml index df5dc24e2cd1..061f3c157a88 100644 --- a/crates/vfs-notify/Cargo.toml +++ b/crates/vfs-notify/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/crates/vfs/Cargo.toml b/crates/vfs/Cargo.toml index d7549a284153..e55bf6f293c4 100644 --- a/crates/vfs/Cargo.toml +++ b/crates/vfs/Cargo.toml @@ -4,7 +4,7 @@ version = "0.0.0" description = "TBD" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [lib] doctest = false diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index e3a4fdfda90c..a07cf036e060 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -481,7 +481,7 @@ It is not cheap enough to enable in prod, and this is a bug which should be fixe rust-analyzer strives to be as configurable as possible while offering reasonable defaults where no configuration exists yet. The rule of thumb is to enable most features by default unless they are buggy or degrade performance too much. There will always be features that some people find more annoying than helpful, so giving the users the ability to tweak or disable these is a big part of offering a good user experience. -Enabling them by default is a matter of discoverability, as many users end up don't know about some features even though they are presented in the manual. +Enabling them by default is a matter of discoverability, as many users don't know about some features even though they are presented in the manual. Mind the code--architecture gap: at the moment, we are using fewer feature flags than we really should. ### Serialization @@ -492,8 +492,8 @@ If a type is serializable, then it is a part of some IPC boundary. You often don't control the other side of this boundary, so changing serializable types is hard. For this reason, the types in `ide`, `base_db` and below are not serializable by design. -If such types need to cross an IPC boundary, then the client of rust-analyzer needs to provide custom, client-specific serialization format. +If such types need to cross an IPC boundary, then the client of rust-analyzer needs to provide a custom, client-specific serialization format. This isolates backwards compatibility and migration concerns to a specific client. -For example, `rust-project.json` is it's own format -- it doesn't include `CrateGraph` as is. +For example, `rust-project.json` is its own format -- it doesn't include `CrateGraph` as is. Instead, it creates a `CrateGraph` by calling appropriate constructing functions. diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 36794efe4272..57f950034cbb 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -173,7 +173,7 @@ Whether to pass `--no-default-features` to Cargo. Defaults to -- Override the command rust-analyzer uses instead of `cargo check` for diagnostics on save. The command is required to output json and -should therefor include `--message-format=json` or a similar option. +should therefore include `--message-format=json` or a similar option. If you're changing this because you're using some tool wrapping Cargo, you might also want to change @@ -190,11 +190,15 @@ cargo check --workspace --message-format=json --all-targets ``` . -- -[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `null`):: +[[rust-analyzer.checkOnSave.target]]rust-analyzer.checkOnSave.target (default: `[]`):: + -- -Check for a specific target. Defaults to -`#rust-analyzer.cargo.target#`. +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"`. -- [[rust-analyzer.completion.autoimport.enable]]rust-analyzer.completion.autoimport.enable (default: `true`):: + @@ -450,6 +454,11 @@ to always show them). -- Whether to show inlay type hints for return types of closures. -- +[[rust-analyzer.inlayHints.expressionAdjustmentHints.enable]]rust-analyzer.inlayHints.expressionAdjustmentHints.enable (default: `"never"`):: ++ +-- +Whether to show inlay hints for type adjustments. +-- [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: + -- @@ -474,7 +483,8 @@ site. [[rust-analyzer.inlayHints.reborrowHints.enable]]rust-analyzer.inlayHints.reborrowHints.enable (default: `"never"`):: + -- -Whether to show inlay type hints for compiler inserted reborrows. +Whether to show inlay hints for compiler inserted reborrows. +This setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#. -- [[rust-analyzer.inlayHints.renderColons]]rust-analyzer.inlayHints.renderColons (default: `true`):: + diff --git a/docs/user/manual.adoc b/docs/user/manual.adoc index 49500e390a50..1a4c70575b03 100644 --- a/docs/user/manual.adoc +++ b/docs/user/manual.adoc @@ -367,7 +367,7 @@ if executable('rust-analyzer') endif ---- -There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <<_configuration,Configuration>> section. +There is no dedicated UI for the server configuration, so you would need to send any options as a value of the `initialization_options` field, as described in the <> section. Here is an example of how to enable the proc-macro support: [source,vim] diff --git a/editors/code/package.json b/editors/code/package.json index 1a97a9c08937..c4d4e428ea07 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -396,6 +396,11 @@ "default": true, "type": "boolean" }, + "rust-analyzer.diagnostics.previewRustcOutput": { + "markdownDescription": "Whether to show the main part of the rendered rustc output of a diagnostic message.", + "default": false, + "type": "boolean" + }, "$generated-start": {}, "rust-analyzer.assist.emitMustUse": { "markdownDescription": "Whether to insert #[must_use] when generating `as_` methods\nfor enum variants.", @@ -623,7 +628,7 @@ ] }, "rust-analyzer.checkOnSave.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefor include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option.\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", "default": null, "type": [ "null", @@ -634,11 +639,18 @@ } }, "rust-analyzer.checkOnSave.target": { - "markdownDescription": "Check for a specific target. Defaults to\n`#rust-analyzer.cargo.target#`.", - "default": null, - "type": [ - "null", - "string" + "markdownDescription": "Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.\n\nCan be a single target, e.g. `\"x86_64-unknown-linux-gnu\"` or a list of targets, e.g.\n`[\"aarch64-apple-darwin\", \"x86_64-apple-darwin\"]`.\n\nAliased as `\"checkOnSave.targets\"`.", + "default": [], + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } ] }, "rust-analyzer.completion.autoimport.enable": { @@ -935,6 +947,21 @@ "Only show type hints for return types of closures with blocks." ] }, + "rust-analyzer.inlayHints.expressionAdjustmentHints.enable": { + "markdownDescription": "Whether to show inlay hints for type adjustments.", + "default": "never", + "type": "string", + "enum": [ + "always", + "never", + "reborrow" + ], + "enumDescriptions": [ + "Always show all adjustment hints.", + "Never show adjustment hints.", + "Only show auto borrow and dereference adjustment hints." + ] + }, "rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", "default": "never", @@ -970,7 +997,7 @@ "type": "boolean" }, "rust-analyzer.inlayHints.reborrowHints.enable": { - "markdownDescription": "Whether to show inlay type hints for compiler inserted reborrows.", + "markdownDescription": "Whether to show inlay hints for compiler inserted reborrows.\nThis setting is deprecated in favor of #rust-analyzer.inlayHints.expressionAdjustmentHints.enable#.", "default": "never", "type": "string", "enum": [ @@ -1276,6 +1303,11 @@ "$generated-end": {} } }, + "configurationDefaults": { + "explorer.fileNesting.patterns": { + "Cargo.toml": "Cargo.lock" + } + }, "problemPatterns": [ { "name": "rustc", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index fb667619c86b..23e039722ee3 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -4,7 +4,7 @@ import * as ra from "../src/lsp_ext"; import * as Is from "vscode-languageclient/lib/common/utils/is"; import { assert } from "./util"; import { WorkspaceEdit } from "vscode"; -import { substituteVSCodeVariables } from "./config"; +import { Config, substituteVSCodeVariables } from "./config"; import { randomUUID } from "crypto"; export interface Env { @@ -66,7 +66,8 @@ export async function createClient( traceOutputChannel: vscode.OutputChannel, outputChannel: vscode.OutputChannel, initializationOptions: vscode.WorkspaceConfiguration, - serverOptions: lc.ServerOptions + serverOptions: lc.ServerOptions, + config: Config ): Promise { const clientOptions: lc.LanguageClientOptions = { documentSelector: [{ scheme: "file", language: "rust" }], @@ -99,6 +100,43 @@ export async function createClient( } }, }, + async handleDiagnostics( + uri: vscode.Uri, + diagnostics: vscode.Diagnostic[], + next: lc.HandleDiagnosticsSignature + ) { + const preview = config.previewRustcOutput; + diagnostics.forEach((diag, idx) => { + // Abuse the fact that VSCode leaks the LSP diagnostics data field through the + // Diagnostic class, if they ever break this we are out of luck and have to go + // back to the worst diagnostics experience ever:) + + // We encode the rendered output of a rustc diagnostic in the rendered field of + // the data payload of the lsp diagnostic. If that field exists, overwrite the + // diagnostic code such that clicking it opens the diagnostic in a readonly + // text editor for easy inspection + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + if (rendered) { + if (preview) { + const index = rendered.match(/^(note|help):/m)?.index || 0; + diag.message = rendered + .substring(0, index) + .replace(/^ -->[^\n]+\n/m, ""); + } + diag.code = { + target: vscode.Uri.from({ + scheme: "rust-analyzer-diagnostics-view", + path: "/diagnostic message", + fragment: uri.toString(), + query: idx.toString(), + }), + value: "Click for full compiler diagnostic", + }; + } + }); + return next(uri, diagnostics); + }, async provideHover( document: vscode.TextDocument, position: vscode.Position, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 632a7d86faa3..d8dbd1df16df 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -238,6 +238,9 @@ export class Config { gotoTypeDef: this.get("hover.actions.gotoTypeDef.enable"), }; } + get previewRustcOutput() { + return this.get("diagnostics.previewRustcOutput"); + } } const VarRegex = new RegExp(/\$\{(.+?)\}/g); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 3e366525ee29..d6cee5c8fc61 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -179,7 +179,8 @@ export class Ctx { this.traceOutputChannel, this.outputChannel, initializationOptions, - serverOptions + serverOptions, + this.config ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index e76b657c1bfb..25f1e83d109c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -48,6 +48,30 @@ async function activateServer(ctx: Ctx): Promise { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider( + "rust-analyzer-diagnostics-view", + new (class implements vscode.TextDocumentContentProvider { + async provideTextDocumentContent(uri: vscode.Uri): Promise { + const diags = ctx.client?.diagnostics?.get( + vscode.Uri.parse(uri.fragment, true) + ); + if (!diags) { + return "Unable to find original rustc diagnostic"; + } + + const diag = diags[parseInt(uri.query)]; + if (!diag) { + return "Unable to find original rustc diagnostic"; + } + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + return rendered ?? "Unable to find original rustc diagnostic"; + } + })() + ) + ); + vscode.workspace.onDidChangeWorkspaceFolders( async (_) => ctx.onWorkspaceFolderChanges(), null, diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 0be0bf920de9..95e27beab5dc 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" publish = false license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.57" +rust-version = "1.65" [dependencies] anyhow = "1.0.62"